file: add g_file_load_bytes()

This adds g_file_load_bytes() to make it more convenient to
load the contents of a GFile as GBytes.

It includes a special casing for gresources to increase the
chances that the GBytes directly references the embedded data
instead of copying to the heap.

https://bugzilla.gnome.org/show_bug.cgi?id=790272
This commit is contained in:
Christian Hergert 2017-11-12 19:55:52 -08:00
parent 5464461e4c
commit 2227918dfd
3 changed files with 207 additions and 0 deletions

View File

@ -184,6 +184,9 @@ g_file_mount_enclosing_volume_finish
g_file_monitor_directory
g_file_monitor_file
g_file_monitor
g_file_load_bytes
g_file_load_bytes_async
g_file_load_bytes_finish
g_file_load_contents
g_file_load_contents_async
g_file_load_contents_finish

View File

@ -8092,3 +8092,191 @@ g_file_supports_thread_contexts (GFile *file)
iface = G_FILE_GET_IFACE (file);
return iface->supports_thread_contexts;
}
/**
* g_file_load_bytes:
* @file: a #GFile
* @cancellable: (nullable): a #GCancellable or %NULL
* @etag_out: (out) (nullable) (optional): a location to place the current
* entity tag for the file, or %NULL if the entity tag is not needed
* @error: a location for a #GError or %NULL
*
* Loads the contents of @file and returns it as #GBytes.
*
* If @file is a resource:// based URI, the resulting bytes will reference the
* embedded resource instead of a copy. Otherwise, this is equivalent to calling
* g_file_load_contents() and g_bytes_new_take().
*
* For resources, @etag_out will be set to %NULL.
*
* The data contained in the resulting #GBytes is always zero-terminated, but
* this is not included in the #GBytes length. The resulting #GBytes should be
* freed with g_bytes_unref() when no longer in use.
*
* Returns: (transfer full): a #GBytes or %NULL and @error is set
*
* Since: 2.56
*/
GBytes *
g_file_load_bytes (GFile *file,
GCancellable *cancellable,
gchar **etag_out,
GError **error)
{
gchar *contents;
gsize len;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
if (etag_out != NULL)
*etag_out = NULL;
if (g_file_has_uri_scheme (file, "resource"))
{
GBytes *bytes;
gchar *uri, *unescaped;
uri = g_file_get_uri (file);
unescaped = g_uri_unescape_string (uri + strlen ("resource://"), NULL);
g_free (uri);
bytes = g_resources_lookup_data (unescaped, G_RESOURCE_LOOKUP_FLAGS_NONE, error);
g_free (unescaped);
return bytes;
}
/* contents is guaranteed to be \0 terminated */
if (g_file_load_contents (file, cancellable, &contents, &len, etag_out, error))
return g_bytes_new_take (g_steal_pointer (&contents), len);
return NULL;
}
static void
g_file_load_bytes_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
GFile *file = G_FILE (object);
GTask *task = user_data;
GError *error = NULL;
gchar *etag = NULL;
gchar *contents = NULL;
gsize len = 0;
g_file_load_contents_finish (file, result, &contents, &len, &etag, &error);
g_task_set_task_data (task, g_steal_pointer (&etag), g_free);
if (error != NULL)
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_return_pointer (task,
g_bytes_new_take (g_steal_pointer (&contents), len),
(GDestroyNotify)g_bytes_unref);
g_object_unref (task);
}
/**
* g_file_load_bytes_async:
* @file: a #GFile
* @cancellable: (nullable): a #GCancellable or %NULL
* @callback: (scope async): a #GAsyncReadyCallback to call when the
* request is satisfied
* @user_data: (closure): the data to pass to callback function
*
* Asynchronously loads the contents of @file as #GBytes.
*
* If @file is a resource:// based URI, the resulting bytes will reference the
* embedded resource instead of a copy. Otherwise, this is equivalent to calling
* g_file_load_contents_async() and g_bytes_new_take().
*
* @callback should call g_file_load_bytes_finish() to get the result of this
* asynchronous operation.
*
* See g_file_load_bytes() for more information.
*
* Since: 2.56
*/
void
g_file_load_bytes_async (GFile *file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
GError *error = NULL;
GBytes *bytes;
GTask *task;
g_return_if_fail (G_IS_FILE (file));
g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
task = g_task_new (file, cancellable, callback, user_data);
g_task_set_source_tag (task, g_file_load_bytes_async);
if (!g_file_has_uri_scheme (file, "resource"))
{
g_file_load_contents_async (file,
cancellable,
g_file_load_bytes_cb,
g_steal_pointer (&task));
return;
}
bytes = g_file_load_bytes (file, cancellable, NULL, &error);
if (bytes == NULL)
g_task_return_error (task, g_steal_pointer (&error));
else
g_task_return_pointer (task,
g_steal_pointer (&bytes),
(GDestroyNotify)g_bytes_unref);
g_object_unref (task);
}
/**
* g_file_load_bytes_finish:
* @file: a #GFile
* @result: a #GAsyncResult provided to the callback
* @etag_out: (out) (nullable) (optional): a location to place the current
* entity tag for the file, or %NULL if the entity tag is not needed
* @error: a location for a #GError, or %NULL
*
* Completes an asynchronous request to g_file_load_bytes_async().
*
* For resources, @etag_out will be set to %NULL.
*
* The data contained in the resulting #GBytes is always zero-terminated, but
* this is not included in the #GBytes length. The resulting #GBytes should be
* freed with g_bytes_unref() when no longer in use.
*
* See g_file_load_bytes() for more information.
*
* Returns: (transfer full): a #GBytes or %NULL and @error is set
*
* Since: 2.56
*/
GBytes *
g_file_load_bytes_finish (GFile *file,
GAsyncResult *result,
gchar **etag_out,
GError **error)
{
GBytes *bytes;
g_return_val_if_fail (G_IS_FILE (file), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
g_return_val_if_fail (g_task_is_valid (G_TASK (result), file), NULL);
g_return_val_if_fail (error == NULL || *error == NULL, NULL);
bytes = g_task_propagate_pointer (G_TASK (result), error);
if (etag_out != NULL)
*etag_out = g_strdup (g_task_get_task_data (G_TASK (result)));
return bytes;
}

View File

@ -1251,6 +1251,22 @@ gboolean g_file_replace_contents_finish (GFile *file,
GLIB_AVAILABLE_IN_ALL
gboolean g_file_supports_thread_contexts (GFile *file);
GLIB_AVAILABLE_IN_2_56
GBytes *g_file_load_bytes (GFile *file,
GCancellable *cancellable,
gchar **etag_out,
GError **error);
GLIB_AVAILABLE_IN_2_56
void g_file_load_bytes_async (GFile *file,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
GLIB_AVAILABLE_IN_2_56
GBytes *g_file_load_bytes_finish (GFile *file,
GAsyncResult *result,
gchar **etag_out,
GError **error);
G_END_DECLS
#endif /* __G_FILE_H__ */