From fcc69fd318f55ca0edc52864b4c0488b304b8382 Mon Sep 17 00:00:00 2001 From: Stef Walter Date: Fri, 11 Nov 2011 17:08:26 +0100 Subject: [PATCH] GBytes: A new type for an immutable set of bytes. * Represents an immutable reference counted block of memory. * This is basically the internal glib GBuffer structure exposed, renamed, and with some additional capabilities. * The GBytes name comes from python3's immutable 'bytes' type * GBytes can be safely used as keys in hash tables, and have functions for doing so: g_bytes_hash, g_bytes_equal * GByteArray is a mutable form of GBytes, and vice versa. There are functions for converting from one to the other efficiently: g_bytes_unref_to_array() and g_byte_array_free_to_bytes() * Adds g_byte_array_new_take() to support above functions https://bugzilla.gnome.org/show_bug.cgi?id=663291 --- docs/reference/glib/Makefile.am | 2 +- docs/reference/glib/glib-sections.txt | 21 +- docs/reference/gobject/gobject-sections.txt | 1 + glib/Makefile.am | 5 +- glib/garray.c | 77 ++++- glib/garray.h | 4 + glib/gbuffer.c | 210 ------------ glib/gbufferprivate.h | 88 ----- glib/gbytes.c | 478 ++++++++++++++++++++++++++++ glib/gbytes.h | 69 ++++ glib/glib.h | 1 + glib/glib.symbols | 16 + glib/gmappedfile.c | 11 - glib/gmappedfile.h | 1 + glib/gtimezone.c | 27 +- glib/gvariant-core.c | 50 +-- glib/gvariant-core.h | 6 +- glib/gvariant.c | 26 +- glib/tests/.gitignore | 1 + glib/tests/Makefile.am | 3 + glib/tests/array-test.c | 33 ++ glib/tests/bytes.c | 331 +++++++++++++++++++ gobject/gboxed.c | 1 + gobject/glib-types.h | 10 + gobject/gobject.symbols | 1 + 25 files changed, 1102 insertions(+), 371 deletions(-) delete mode 100644 glib/gbuffer.c delete mode 100644 glib/gbufferprivate.h create mode 100644 glib/gbytes.c create mode 100644 glib/gbytes.h create mode 100644 glib/tests/bytes.c diff --git a/docs/reference/glib/Makefile.am b/docs/reference/glib/Makefile.am index 405ac16..4dd4ee9 100644 --- a/docs/reference/glib/Makefile.am +++ b/docs/reference/glib/Makefile.am @@ -46,7 +46,7 @@ IGNORE_HFILES = \ gnulib \ pcre \ update-pcre \ - gbufferprivate.h \ + gbytesprivate.h \ gvariant-internal.h \ gvariant-serialiser.h \ gvariant-core.h \ diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 7044843..8e88d73 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -2382,8 +2382,10 @@ g_ptr_array_foreach
Byte Arrays arrays_byte + GByteArray g_byte_array_new +g_byte_array_new_take g_byte_array_sized_new g_byte_array_ref g_byte_array_unref @@ -2396,7 +2398,24 @@ g_byte_array_sort g_byte_array_sort_with_data g_byte_array_set_size g_byte_array_free - +g_byte_array_free_to_bytes + + +GBytes +g_bytes_new +g_bytes_new_take +g_bytes_new_static +g_bytes_new_with_free_func +g_bytes_new_from_bytes +g_bytes_get_data +g_bytes_get_size +g_bytes_hash +g_bytes_equal +g_bytes_compare +g_bytes_ref +g_bytes_unref +g_bytes_unref_to_data +g_bytes_unref_to_array
diff --git a/docs/reference/gobject/gobject-sections.txt b/docs/reference/gobject/gobject-sections.txt index 2a5daa8..e466d57 100644 --- a/docs/reference/gobject/gobject-sections.txt +++ b/docs/reference/gobject/gobject-sections.txt @@ -349,6 +349,7 @@ G_TYPE_MATCH_INFO G_TYPE_ARRAY G_TYPE_BYTE_ARRAY G_TYPE_PTR_ARRAY +G_TYPE_BYTES G_TYPE_VARIANT_TYPE G_TYPE_ERROR G_TYPE_DATE_TIME diff --git a/glib/Makefile.am b/glib/Makefile.am index 5e9f31a..1b427c3 100644 --- a/glib/Makefile.am +++ b/glib/Makefile.am @@ -130,8 +130,8 @@ libglib_2_0_la_SOURCES = \ gbitlock.c \ gbookmarkfile.c \ gbsearcharray.h \ - gbuffer.c \ - gbufferprivate.h \ + gbytes.c \ + gbytes.h \ gcharset.c \ gchecksum.c \ gconvert.c \ @@ -262,6 +262,7 @@ glibsubinclude_HEADERS = \ gbase64.h \ gbitlock.h \ gbookmarkfile.h \ + gbytes.h \ gcharset.h \ gchecksum.h \ gconvert.h \ diff --git a/glib/garray.c b/glib/garray.c index 27bc7e4..f46656e 100644 --- a/glib/garray.c +++ b/glib/garray.c @@ -35,8 +35,10 @@ #include "garray.h" +#include "gbytes.h" #include "gslice.h" #include "gmem.h" +#include "gtestutils.h" #include "gthread.h" #include "gmessages.h" #include "gqsort.h" @@ -1344,16 +1346,13 @@ g_ptr_array_foreach (GPtrArray *array, /** * SECTION:arrays_byte * @title: Byte Arrays - * @short_description: arrays of bytes, which grow automatically as - * elements are added + * @short_description: arrays of bytes * - * #GByteArray is based on #GArray, to provide arrays of bytes which - * grow automatically as elements are added. + * #GByteArray is a mutable array of bytes based on #GArray, to provide arrays + * of bytes which grow automatically as elements are added. * - * To create a new #GByteArray use g_byte_array_new(). - * - * To add elements to a #GByteArray, use g_byte_array_append(), and - * g_byte_array_prepend(). + * To create a new #GByteArray use g_byte_array_new(). To add elements to a + * #GByteArray, use g_byte_array_append(), and g_byte_array_prepend(). * * To set the size of a #GByteArray, use g_byte_array_set_size(). * @@ -1380,6 +1379,9 @@ g_ptr_array_foreach (GPtrArray *array, * g_byte_array_free (gbarray, TRUE); * * + * + * See #GBytes if you are interested in an immutable object representing a + * sequence of bytes. **/ /** @@ -1404,6 +1406,36 @@ GByteArray* g_byte_array_new (void) } /** + * g_byte_array_new_take: + * @data: (array length=len): byte data for the array + * @len: length of @data + * + * Create byte array containing the data. The data will be owned by the array + * and will be freed with g_free(), i.e. it could be allocated using g_strdup(). + * + * Since: 2.32 + * + * Returns: (transfer full): a new #GByteArray + */ +GByteArray * +g_byte_array_new_take (guint8 *data, + gsize len) +{ + GByteArray *array; + GRealArray *real; + + array = g_byte_array_new (); + real = (GRealArray *)array; + g_assert (real->data == NULL); + g_assert (real->len == 0); + + real->data = data; + real->len = len; + + return array; +} + +/** * g_byte_array_sized_new: * @reserved_size: number of bytes preallocated. * @Returns: the new #GByteArray. @@ -1437,6 +1469,35 @@ guint8* g_byte_array_free (GByteArray *array, } /** + * g_byte_array_free_to_bytes: + * @array: (transfer full): a #GByteArray + * + * Transfers the data from the #GByteArray into a new immutable #GBytes. + * + * The #GByteArray is freed unless the reference count of @array is greater + * than one, the #GByteArray wrapper is preserved but the size of @array + * will be set to zero. + * + * This is identical to using g_bytes_new_take() and g_byte_array_free() + * together. + * + * Since: 2.32 + * + * Returns: (transfer full): a new immutable #GBytes representing same byte + * data that was in the array + */ +GBytes * +g_byte_array_free_to_bytes (GByteArray *array) +{ + gsize length; + + g_return_val_if_fail (array != NULL, NULL); + + length = array->len; + return g_bytes_new_take (g_byte_array_free (array, FALSE), length); +} + +/** * g_byte_array_ref: * @array: A #GByteArray. * diff --git a/glib/garray.h b/glib/garray.h index 3152b97..d96aade 100644 --- a/glib/garray.h +++ b/glib/garray.h @@ -35,6 +35,7 @@ G_BEGIN_DECLS +typedef struct _GBytes GBytes; typedef struct _GArray GArray; typedef struct _GByteArray GByteArray; typedef struct _GPtrArray GPtrArray; @@ -150,9 +151,12 @@ void g_ptr_array_foreach (GPtrArray *array, */ GByteArray* g_byte_array_new (void); +GByteArray* g_byte_array_new_take (guint8 *data, + gsize len); GByteArray* g_byte_array_sized_new (guint reserved_size); guint8* g_byte_array_free (GByteArray *array, gboolean free_segment); +GBytes* g_byte_array_free_to_bytes (GByteArray *array); GByteArray *g_byte_array_ref (GByteArray *array); void g_byte_array_unref (GByteArray *array); GByteArray* g_byte_array_append (GByteArray *array, diff --git a/glib/gbuffer.c b/glib/gbuffer.c deleted file mode 100644 index bfe67d2..0000000 --- a/glib/gbuffer.c +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright © 2009, 2010 Codethink Limited - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: Ryan Lortie - */ - -#include "config.h" - -#include "gbufferprivate.h" - -#include -#include -#include -#include - -typedef struct -{ - GBuffer buffer; - - GDestroyNotify user_destroy; - gpointer user_data; -} GUserNotifyBuffer; - -static void -g_buffer_free_gfree (GBuffer *buffer) -{ - g_free ((gpointer) buffer->data); - g_slice_free (GBuffer, buffer); -} - -/* < private > - * g_buffer_new_from_data: - * @data: the data to be used for the buffer - * @size: the size of @data - * - * Creates a new #GBuffer from @data. - * - * @data is copied. - * - * Returns: a reference to a new #GBuffer - */ - -GBuffer * -g_buffer_new_from_data (gconstpointer data, - gsize size) -{ - GBuffer *buffer; - - buffer = g_slice_new (GBuffer); - buffer->data = g_memdup (data, size); - buffer->size = size; - buffer->free_func = g_buffer_free_gfree; - buffer->ref_count = 1; - - return buffer; -} - -/* < private > - * g_buffer_new_take_data: - * @data: the data to be used for the buffer - * @size: the size of @data - * returns: a reference to a new #GBuffer - * - * Creates a new #GBuffer from @data. - * - * @data must have been created by a call to g_malloc(), g_malloc0() or - * g_realloc() or by one of the many functions that wrap these calls - * (such as g_new(), g_strdup(), etc). - * - * After this call, @data belongs to the buffer and may no longer be - * modified by the caller. g_free() will be called on @data when the - * buffer is no longer in use. - */ -GBuffer * -g_buffer_new_take_data (gpointer data, - gsize size) -{ - GBuffer *buffer; - - buffer = g_slice_new (GBuffer); - buffer->data = data; - buffer->size = size; - buffer->free_func = g_buffer_free_gfree; - buffer->ref_count = 1; - - return buffer; -} - -static void -g_buffer_free (GBuffer *buffer) -{ - g_slice_free (GBuffer, buffer); -} - -/* < private > - * g_buffer_new_from_static_data: - * @data: the data to be used for the buffer - * @size: the size of @data - * - * Creates a new #GBuffer from static data. - * - * @data must be static (ie: never modified or freed). - * - * Returns: a reference to a new #GBuffer - */ -GBuffer * -g_buffer_new_from_static_data (gconstpointer data, - gsize size) -{ - GBuffer *buffer; - - buffer = g_slice_new (GBuffer); - buffer->data = data; - buffer->size = size; - buffer->free_func = g_buffer_free; - buffer->ref_count = 1; - - return buffer; -} - -static void -g_buffer_free_usernotify (GBuffer *buffer) -{ - GUserNotifyBuffer *ubuffer = (GUserNotifyBuffer *) buffer; - - ubuffer->user_destroy (ubuffer->user_data); - g_slice_free (GUserNotifyBuffer, ubuffer); -} - -/* < private > - * g_buffer_new_from_pointer: - * @data: the data to be used for the buffer - * @size: the size of @data - * @notify: the function to call to release the data - * @user_data: the data to pass to @notify - * - * Creates a #GBuffer from @data. - * - * When the last reference is dropped, @notify will be called on - * @user_data. - * - * @data must not be modified after this call is made, until @notify has - * been called to indicate that the buffer is no longer in use. - * - * Returns: a reference to a new #GBuffer - */ -GBuffer * -g_buffer_new_from_pointer (gconstpointer data, - gsize size, - GDestroyNotify notify, - gpointer user_data) -{ - GUserNotifyBuffer *ubuffer; - - ubuffer = g_slice_new (GUserNotifyBuffer); - ubuffer->buffer.data = data; - ubuffer->buffer.size = size; - ubuffer->buffer.free_func = g_buffer_free_usernotify; - ubuffer->buffer.ref_count = 1; - ubuffer->user_destroy = notify; - ubuffer->user_data = user_data; - - return (GBuffer *) ubuffer; -} - -/* < private > - * g_buffer_ref: - * @buffer: a #GBuffer - * - * Increase the reference count on @buffer. - * - * Returns: @buffer - */ -GBuffer * -g_buffer_ref (GBuffer *buffer) -{ - g_atomic_int_inc (&buffer->ref_count); - - return buffer; -} - -/* < private > - * g_buffer_unref: - * @buffer: a #GBuffer - * - * Releases a reference on @buffer. This may result in the buffer being - * freed. - */ -void -g_buffer_unref (GBuffer *buffer) -{ - if (g_atomic_int_dec_and_test (&buffer->ref_count)) - if (buffer->free_func != NULL) - buffer->free_func (buffer); -} diff --git a/glib/gbufferprivate.h b/glib/gbufferprivate.h deleted file mode 100644 index 6bb29b7..0000000 --- a/glib/gbufferprivate.h +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright © 2009, 2010 Codethink Limited - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2 of the licence, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the - * Free Software Foundation, Inc., 59 Temple Place - Suite 330, - * Boston, MA 02111-1307, USA. - * - * Author: Ryan Lortie - */ - -#ifndef __G_BUFFER_H__ -#define __G_BUFFER_H__ - -#include - -/* < private > - * GBuffer: - * @data: a pointer to the data held in the buffer - * @size: the size of @data - * - * A simple refcounted data type representing a byte sequence from an - * unspecified origin. - * - * The purpose of a #GBuffer is to keep the memory region that it holds - * alive for as long as anyone holds a reference to the buffer. When - * the last reference count is dropped, the memory is released. - * - * A #GBuffer can come from many different origins that may have - * different procedures for freeing the memory region. Examples are - * memory from g_malloc(), from memory slices, from a #GMappedFile or - * memory from other allocators. - */ -typedef struct _GBuffer GBuffer; - -/* < private > - * GBufferFreeFunc: - * @buffer: the #GBuffer to be freed - * - * This function is provided by creators of a #GBuffer. It is the - * function to be called when the reference count of @buffer drops to - * zero. It should free any memory associated with the buffer and free - * @buffer itself. - */ -typedef void (* GBufferFreeFunc) (GBuffer *buffer); - -struct _GBuffer -{ - gconstpointer data; - gsize size; - - /*< protected >*/ - GBufferFreeFunc free_func; - - /*< private >*/ - gint ref_count; -}; - -G_GNUC_INTERNAL -GBuffer * g_buffer_new_from_data (gconstpointer data, - gsize size); -G_GNUC_INTERNAL -GBuffer * g_buffer_new_take_data (gpointer data, - gsize size); -G_GNUC_INTERNAL -GBuffer * g_buffer_new_from_static_data (gconstpointer data, - gsize size); -G_GNUC_INTERNAL -GBuffer * g_buffer_new_from_pointer (gconstpointer data, - gsize size, - GDestroyNotify notify, - gpointer user_data); -G_GNUC_INTERNAL -GBuffer * g_buffer_ref (GBuffer *buffer); -G_GNUC_INTERNAL -void g_buffer_unref (GBuffer *buffer); - -#endif /* __G_BUFFER_H__ */ diff --git a/glib/gbytes.c b/glib/gbytes.c new file mode 100644 index 0000000..dcf0dfb --- /dev/null +++ b/glib/gbytes.c @@ -0,0 +1,478 @@ +/* + * Copyright © 2009, 2010 Codethink Limited + * Copyright © 2011 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Ryan Lortie + * Stef Walter + */ + +#include "config.h" + +#include "gbytes.h" + +#include +#include +#include +#include +#include +#include +#include + +#include + +/** + * GBytes: + * + * A simple refcounted data type representing an immutable byte sequence + * from an unspecified origin. + * + * The purpose of a #GBytes is to keep the memory region that it holds + * alive for as long as anyone holds a reference to the bytes. When + * the last reference count is dropped, the memory is released. Multiple + * unrelated callers can use byte data in the #GBytes without coordinating + * their activities, resting assured that the byte data will not change or + * move while they hold a reference. + * + * A #GBytes can come from many different origins that may have + * different procedures for freeing the memory region. Examples are + * memory from g_malloc(), from memory slices, from a #GMappedFile or + * memory from other allocators. + * + * #GBytes work well as keys in #GHashTable. Use g_bytes_equal() and + * g_bytes_hash() as parameters to g_hash_table_new() or g_hash_table_new_full(). + * #GBytes can also be used as keys in a #GTree by passing the g_bytes_compare() + * function to g_tree_new(). + * + * The data pointed to by this bytes must not be modified. For a mutable + * array of bytes see #GByteArray. Use g_bytes_unref_to_array() to create a + * mutable array for a #GBytes sequence. To create an immutable #GBytes from + * a mutable #GByteArray, use the g_byte_array_free_to_bytes() function. + * + * Since: 2.32 + **/ + +struct _GBytes +{ + gconstpointer data; + gsize size; + gint ref_count; + GDestroyNotify free_func; + gpointer user_data; +}; + +/** + * g_bytes_new: + * @data: (array length=size): the data to be used for the bytes + * @size: the size of @data + * + * Creates a new #GBytes from @data. + * + * @data is copied. + * + * Returns: (transfer full): a new #GBytes + * + * Since: 2.32 + */ +GBytes * +g_bytes_new (gconstpointer data, + gsize size) +{ + return g_bytes_new_take (g_memdup (data, size), size); +} + +/** + * g_bytes_new_take: + * @data: (transfer full) (array length=size): the data to be used for the bytes + * @size: the size of @data + * + * Creates a new #GBytes from @data. + * + * After this call, @data belongs to the bytes and may no longer be + * modified by the caller. g_free() will be called on @data when the + * bytes is no longer in use. Because of this @data must have been created by + * a call to g_malloc(), g_malloc0() or g_realloc() or by one of the many + * functions that wrap these calls (such as g_new(), g_strdup(), etc). + * + * For creating #GBytes with memory from other allocators, see + * g_bytes_new_with_free_func(). + * + * Returns: (transfer full): a new #GBytes + * + * Since: 2.32 + */ +GBytes * +g_bytes_new_take (gpointer data, + gsize size) +{ + return g_bytes_new_with_free_func (data, size, g_free, data); +} + + +/** + * g_bytes_new_static: + * @data: (array length=size): the data to be used for the bytes + * @size: the size of @data + * + * Creates a new #GBytes from static data. + * + * @data must be static (ie: never modified or freed). + * + * Returns: (transfer full): a new #GBytes + * + * Since: 2.32 + */ +GBytes * +g_bytes_new_static (gconstpointer data, + gsize size) +{ + return g_bytes_new_with_free_func (data, size, NULL, NULL); +} + +/** + * g_bytes_new_with_free_func: + * @data: (array length=size): the data to be used for the bytes + * @size: the size of @data + * @free_func: the function to call to release the data + * @user_data: data to pass to @free_func + * + * Creates a #GBytes from @data. + * + * When the last reference is dropped, @free_func will be called with the + * @user_data argument. + * + * @data must not be modified after this call is made until @free_func has + * been called to indicate that the bytes is no longer in use. + * + * Returns: (transfer full): a new #GBytes + * + * Since: 2.32 + */ +GBytes * +g_bytes_new_with_free_func (gconstpointer data, + gsize size, + GDestroyNotify free_func, + gpointer user_data) +{ + GBytes *bytes; + + bytes = g_slice_new (GBytes); + bytes->data = data; + bytes->size = size; + bytes->free_func = free_func; + bytes->user_data = user_data; + bytes->ref_count = 1; + + return (GBytes *)bytes; +} + +/** + * g_bytes_new_from_bytes: + * @bytes: a #GBytes + * @offset: offset which subsection starts at + * @length: length of subsection + * + * Creates a #GBytes which is a subsection of another #GBytes. The @offset + + * @length may not be longer than the size of @bytes. + * + * A reference to @bytes will be held by the newly created #GBytes until + * the byte data is no longer needed. + * + * Returns: (transfer full): a new #GBytes + * + * Since: 2.32 + */ +GBytes * +g_bytes_new_from_bytes (GBytes *bytes, + gsize offset, + gsize length) +{ + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (offset <= bytes->size, NULL); + g_return_val_if_fail (offset + length <= bytes->size, NULL); + + return g_bytes_new_with_free_func ((gchar *)bytes->data + offset, length, + (GDestroyNotify)g_bytes_unref, g_bytes_ref (bytes)); +} + +/** + * g_bytes_get_data: + * @bytes: a #GBytes + * + * Get the byte data in the #GBytes. This data should not be modified. + * + * This function will always return the same pointer for a given #GBytes. + * + * Returns: a pointer to the byte data + * + * Since: 2.32 + */ +gconstpointer +g_bytes_get_data (GBytes *bytes) +{ + g_return_val_if_fail (bytes != NULL, NULL); + return bytes->data; +} + +/** + * g_bytes_get_size: + * @bytes: a #GBytes + * + * Get the size of the byte data in the #GBytes. + * + * This function will always return the same value for a given #GBytes. + * + * Returns: the size + * + * Since: 2.32 + */ +gsize +g_bytes_get_size (GBytes *bytes) +{ + g_return_val_if_fail (bytes != NULL, 0); + return bytes->size; +} + + +/** + * g_bytes_ref: + * @bytes: a #GBytes + * + * Increase the reference count on @bytes. + * + * Returns: the #GBytes + * + * Since: 2.32 + */ +GBytes * +g_bytes_ref (GBytes *bytes) +{ + g_return_val_if_fail (bytes != NULL, NULL); + + g_atomic_int_inc (&bytes->ref_count); + + return bytes; +} + +/** + * g_bytes_unref: + * @bytes: (allow-none): a #GBytes + * + * Releases a reference on @bytes. This may result in the bytes being + * freed. + * + * Since: 2.32 + */ +void +g_bytes_unref (GBytes *bytes) +{ + if (bytes == NULL) + return; + + if (g_atomic_int_dec_and_test (&bytes->ref_count)) + { + if (bytes->free_func != NULL) + bytes->free_func (bytes->user_data); + g_slice_free (GBytes, bytes); + } +} + +/** + * g_bytes_equal: + * @bytes1: (type GLib.Bytes): a pointer to a #GBytes + * @bytes2: (type GLib.Bytes): a pointer to a #GBytes to compare with @bytes1 + * + * Compares the two #GBytes values being pointed to and returns + * %TRUE if they are equal. + * + * This function can be passed to g_hash_table_new() as the @key_equal_func + * parameter, when using non-%NULL #GBytes pointers as keys in a #GHashTable. + * + * Returns: %TRUE if the two keys match. + * + * Since: 2.32 + */ +gboolean +g_bytes_equal (gconstpointer bytes1, + gconstpointer bytes2) +{ + const GBytes *b1 = bytes1; + const GBytes *b2 = bytes2; + + g_return_val_if_fail (bytes1 != NULL, FALSE); + g_return_val_if_fail (bytes2 != NULL, FALSE); + + return b1->size == b2->size && + memcmp (b1->data, b2->data, b1->size) == 0; +} + +/** + * g_bytes_hash: + * @bytes: (type GLib.Bytes): a pointer to a #GBytes key + * + * Creates an integer hash code for the byte data in the #GBytes. + * + * This function can be passed to g_hash_table_new() as the @key_equal_func + * parameter, when using non-%NULL #GBytes pointers as keys in a #GHashTable. + * + * Returns: a hash value corresponding to the key. + * + * Since: 2.32 + */ +guint +g_bytes_hash (gconstpointer bytes) +{ + const GBytes *a = bytes; + const signed char *p, *e; + guint32 h = 5381; + + g_return_val_if_fail (bytes != NULL, 0); + + for (p = (signed char *)a->data, e = (signed char *)a->data + a->size; p != e; p++) + h = (h << 5) + h + *p; + + return h; +} + +/** + * g_bytes_compare: + * @bytes1: (type GLib.Bytes): a pointer to a #GBytes + * @bytes2: (type GLib.Bytes): a pointer to a #GBytes to compare with @bytes1 + * + * Compares the two #GBytes values. + * + * This function can be used to sort GBytes instances in lexographical order. + * + * Returns: a negative value if bytes2 is lesser, a positive value if bytes2 is + * greater, and zero if bytes2 is equal to bytes1 + * + * Since: 2.32 + */ +gint +g_bytes_compare (gconstpointer bytes1, + gconstpointer bytes2) +{ + const GBytes *b1 = bytes1; + const GBytes *b2 = bytes2; + gint ret; + + g_return_val_if_fail (bytes1 != NULL, 0); + g_return_val_if_fail (bytes2 != NULL, 0); + + ret = memcmp (b1->data, b2->data, MIN (b1->size, b2->size)); + if (ret == 0 && b1->size != b2->size) + ret = b1->size < b2->size ? -1 : 1; + return ret; +} + +static gpointer +try_steal_and_unref (GBytes *bytes, + GDestroyNotify free_func, + gsize *size) +{ + gpointer result; + + if (bytes->free_func != free_func) + return NULL; + + /* Are we the only reference? */ + if (g_atomic_int_get (&bytes->ref_count) == 1) + { + *size = bytes->size; + result = (gpointer)bytes->data; + g_slice_free (GBytes, bytes); + return result; + } + + return NULL; +} + + +/** + * g_bytes_unref_to_data: + * @bytes: (transfer full): a #GBytes + * @size: location to place the length of the returned data + * + * Unreferences the bytes, and returns a pointer the same byte data + * contents. + * + * As an optimization, the byte data is returned without copying if this was + * the last reference to bytes and bytes was created with g_bytes_new(), + * g_bytes_new_take() or g_byte_array_free_to_bytes(). In all other cases the + * data is copied. + * + * Returns: (transfer full): a pointer to the same byte data, which should + * be freed with g_free() + * + * Since: 2.32 + */ +gpointer +g_bytes_unref_to_data (GBytes *bytes, + gsize *size) +{ + gpointer result; + + g_return_val_if_fail (bytes != NULL, NULL); + g_return_val_if_fail (size != NULL, NULL); + + /* + * Optimal path: if this is was the last reference, then we can return + * the data from this GBytes without copying. + */ + + result = try_steal_and_unref (bytes, g_free, size); + if (result == NULL) + { + /* + * Copy: Non g_malloc (or compatible) allocator, or static memory, + * so we have to copy, and then unref. + */ + result = g_memdup (bytes->data, bytes->size); + *size = bytes->size; + g_bytes_unref (bytes); + } + + return result; +} + +/** + * g_bytes_unref_to_array: + * @bytes: (transfer full): a #GBytes + * + * Unreferences the bytes, and returns a new mutable #GByteArray containing + * the same byte data. + * + * As an optimization, the byte data is transferred to the array without copying + * if this was the last reference to bytes and bytes was created with + * g_bytes_new(), g_bytes_new_take() or g_byte_array_free_to_bytes(). In all + * other cases the data is copied. + * + * Returns: (transfer full): a new mutable #GByteArray containing the same byte data + * + * Since: 2.32 + */ +GByteArray * +g_bytes_unref_to_array (GBytes *bytes) +{ + gpointer data; + gsize size; + + g_return_val_if_fail (bytes != NULL, NULL); + + data = g_bytes_unref_to_data (bytes, &size); + return g_byte_array_new_take (data, size); +} diff --git a/glib/gbytes.h b/glib/gbytes.h new file mode 100644 index 0000000..cf7b326 --- /dev/null +++ b/glib/gbytes.h @@ -0,0 +1,69 @@ +/* + * Copyright © 2009, 2010 Codethink Limited + * Copyright © 2011 Collabora Ltd. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the licence, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + * + * Author: Ryan Lortie + * Stef Walter + */ + +#ifndef __G_BYTES_H__ +#define __G_BYTES_H__ + +#include +#include + +GBytes * g_bytes_new (gconstpointer data, + gsize size); + +GBytes * g_bytes_new_take (gpointer data, + gsize size); + +GBytes * g_bytes_new_static (gconstpointer data, + gsize size); + +GBytes * g_bytes_new_with_free_func (gconstpointer data, + gsize size, + GDestroyNotify free_func, + gpointer user_data); + +GBytes * g_bytes_new_from_bytes (GBytes *bytes, + gsize offset, + gsize length); + +gconstpointer g_bytes_get_data (GBytes *bytes); + +gsize g_bytes_get_size (GBytes *bytes); + +GBytes * g_bytes_ref (GBytes *bytes); + +void g_bytes_unref (GBytes *bytes); + +gpointer g_bytes_unref_to_data (GBytes *bytes, + gsize *size); + +GByteArray * g_bytes_unref_to_array (GBytes *bytes); + +guint g_bytes_hash (gconstpointer bytes); + +gboolean g_bytes_equal (gconstpointer bytes1, + gconstpointer bytes2); + +gint g_bytes_compare (gconstpointer bytes1, + gconstpointer bytes2); + +#endif /* __G_BYTES_H__ */ diff --git a/glib/glib.h b/glib/glib.h index 5e4a528..06056a3 100644 --- a/glib/glib.h +++ b/glib/glib.h @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include diff --git a/glib/glib.symbols b/glib/glib.symbols index c78d1f8..15d6616 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -18,9 +18,11 @@ g_array_sort g_array_sort_with_data g_byte_array_append g_byte_array_free +g_byte_array_free_to_bytes g_byte_array_unref g_byte_array_ref g_byte_array_new +g_byte_array_new_take g_byte_array_prepend g_byte_array_remove_index g_byte_array_remove_index_fast @@ -29,6 +31,20 @@ g_byte_array_set_size g_byte_array_sized_new g_byte_array_sort g_byte_array_sort_with_data +g_bytes_compare +g_bytes_equal +g_bytes_get_data +g_bytes_get_size +g_bytes_hash +g_bytes_new +g_bytes_new_from_bytes +g_bytes_new_static +g_bytes_new_take +g_bytes_new_with_free_func +g_bytes_ref +g_bytes_unref +g_bytes_unref_to_array +g_bytes_unref_to_data g_ptr_array_add g_ptr_array_foreach g_ptr_array_free diff --git a/glib/gmappedfile.c b/glib/gmappedfile.c index 4a73827..a303a36 100644 --- a/glib/gmappedfile.c +++ b/glib/gmappedfile.c @@ -56,7 +56,6 @@ #include "gstdio.h" #include "gstrfuncs.h" #include "gatomic.h" -#include "gbufferprivate.h" #include "glibintl.h" @@ -88,16 +87,6 @@ struct _GMappedFile #endif }; -/* Make sure the layout of GMappedFile is the same as GBuffer's */ -G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, contents) == - G_STRUCT_OFFSET (GBuffer, data)); -G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, length) == - G_STRUCT_OFFSET (GBuffer, size)); -G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, ref_count) == - G_STRUCT_OFFSET (GBuffer, ref_count)); -G_STATIC_ASSERT (G_STRUCT_OFFSET (GMappedFile, free_func) == - G_STRUCT_OFFSET (GBuffer, free_func)); - static void g_mapped_file_destroy (GMappedFile *file) { diff --git a/glib/gmappedfile.h b/glib/gmappedfile.h index f48b141..52ba31d 100644 --- a/glib/gmappedfile.h +++ b/glib/gmappedfile.h @@ -26,6 +26,7 @@ #ifndef __G_MAPPED_FILE_H__ #define __G_MAPPED_FILE_H__ +#include #include G_BEGIN_DECLS diff --git a/glib/gtimezone.c b/glib/gtimezone.c index efb2733..19358b1 100644 --- a/glib/gtimezone.c +++ b/glib/gtimezone.c @@ -33,7 +33,7 @@ #include "gstrfuncs.h" #include "ghash.h" #include "gthread.h" -#include "gbufferprivate.h" +#include "gbytes.h" #include "gslice.h" /** @@ -118,7 +118,7 @@ struct _GTimeZone { gchar *name; - GBuffer *zoneinfo; + GBytes *zoneinfo; const struct tzhead *header; const struct ttinfo *infos; @@ -169,7 +169,7 @@ again: } if (tz->zoneinfo) - g_buffer_unref (tz->zoneinfo); + g_bytes_unref (tz->zoneinfo); g_free (tz->name); @@ -269,7 +269,7 @@ parse_constant_offset (const gchar *name, } } -static GBuffer * +static GBytes * zone_for_constant_offset (const gchar *name) { const gchar fake_zoneinfo_headers[] = @@ -296,7 +296,7 @@ zone_for_constant_offset (const gchar *name) fake->info.tt_abbrind = 0; strcpy (fake->abbr, name); - return g_buffer_new_take_data (fake, sizeof *fake); + return g_bytes_new_take (fake, sizeof *fake); } /* Construction {{{1 */ @@ -345,6 +345,7 @@ GTimeZone * g_time_zone_new (const gchar *identifier) { GTimeZone *tz; + GMappedFile *file; G_LOCK (time_zones); if (time_zones == NULL) @@ -380,19 +381,27 @@ g_time_zone_new (const gchar *identifier) else filename = g_strdup ("/etc/localtime"); - tz->zoneinfo = (GBuffer *) g_mapped_file_new (filename, FALSE, NULL); + file = g_mapped_file_new (filename, FALSE, NULL); + if (file != NULL) + { + tz->zoneinfo = g_bytes_new_with_free_func (g_mapped_file_get_contents (file), + g_mapped_file_get_length (file), + (GDestroyNotify)g_mapped_file_unref, + g_mapped_file_ref (file)); + g_mapped_file_unref (file); + } g_free (filename); } if (tz->zoneinfo != NULL) { - const struct tzhead *header = tz->zoneinfo->data; - gsize size = tz->zoneinfo->size; + const struct tzhead *header = g_bytes_get_data (tz->zoneinfo); + gsize size = g_bytes_get_size (tz->zoneinfo); /* we only bother to support version 2 */ if (size < sizeof (struct tzhead) || memcmp (header, "TZif2", 5)) { - g_buffer_unref (tz->zoneinfo); + g_bytes_unref (tz->zoneinfo); tz->zoneinfo = NULL; } else diff --git a/glib/gvariant-core.c b/glib/gvariant-core.c index 6d1e2b3..436d4bf 100644 --- a/glib/gvariant-core.c +++ b/glib/gvariant-core.c @@ -24,7 +24,7 @@ #include #include #include -#include +#include #include #include #include @@ -61,7 +61,7 @@ struct _GVariant { struct { - GBuffer *buffer; + GBytes *bytes; gconstpointer data; } serialised; @@ -133,16 +133,16 @@ struct _GVariant * never be changed. It is therefore valid to access * them without holding a lock. * - * .buffer: the #GBuffer that contains the memory pointed to by + * .bytes: the #GBytes that contains the memory pointed to by * .data, or %NULL if .data is %NULL. In the event that * the instance was deserialised from another instance, - * then the buffer will be shared by both of them. When + * then the bytes will be shared by both of them. When * the instance is freed, this reference must be released - * with g_buffer_unref(). + * with g_bytes_unref(). * * .data: the serialised data (of size 'size') of the instance. * This pointer should not be freed or modified in any way. - * #GBuffer is responsible for memory management. + * #GBytes is responsible for memory management. * * This pointer may be %NULL in two cases: * @@ -438,7 +438,7 @@ g_variant_ensure_serialised (GVariant *value) if (~value->state & STATE_SERIALISED) { - GBuffer *buffer; + GBytes *bytes; gpointer data; g_variant_ensure_size (value); @@ -447,9 +447,9 @@ g_variant_ensure_serialised (GVariant *value) g_variant_release_children (value); - buffer = g_buffer_new_take_data (data, value->size); - value->contents.serialised.data = buffer->data; - value->contents.serialised.buffer = buffer; + bytes = g_bytes_new_take (data, value->size); + value->contents.serialised.data = g_bytes_get_data (bytes); + value->contents.serialised.bytes = bytes; value->state |= STATE_SERIALISED; } } @@ -486,23 +486,23 @@ g_variant_alloc (const GVariantType *type, /* -- internal -- */ /* < internal > - * g_variant_new_from_buffer: + * g_variant_new_from_bytes: * @type: a #GVariantType - * @buffer: a #GBuffer - * @trusted: if the contents of @buffer are trusted + * @bytes: a #GBytes + * @trusted: if the contents of @bytes are trusted * * Constructs a new serialised-mode #GVariant instance. This is the * inner interface for creation of new serialised values that gets * called from various functions in gvariant.c. * - * A reference is taken on @buffer. + * A reference is taken on @bytes. * * Returns: a new #GVariant with a floating reference */ GVariant * -g_variant_new_from_buffer (const GVariantType *type, - GBuffer *buffer, - gboolean trusted) +g_variant_new_from_bytes (const GVariantType *type, + GBytes *bytes, + gboolean trusted) { GVariant *value; guint alignment; @@ -510,14 +510,14 @@ g_variant_new_from_buffer (const GVariantType *type, value = g_variant_alloc (type, TRUE, trusted); - value->contents.serialised.buffer = g_buffer_ref (buffer); + value->contents.serialised.bytes = g_bytes_ref (bytes); g_variant_type_info_query (value->type_info, &alignment, &size); - if (size && buffer->size != size) + if (size && g_bytes_get_size (bytes) != size) { - /* Creating a fixed-sized GVariant with a buffer of the wrong + /* Creating a fixed-sized GVariant with a bytes of the wrong * size. * * We should do the equivalent of pulling a fixed-sized child out @@ -529,8 +529,8 @@ g_variant_new_from_buffer (const GVariantType *type, } else { - value->contents.serialised.data = buffer->data; - value->size = buffer->size; + value->contents.serialised.data = g_bytes_get_data (bytes); + value->size = g_bytes_get_size (bytes); } return value; @@ -630,7 +630,7 @@ g_variant_unref (GVariant *value) g_variant_type_info_unref (value->type_info); if (value->state & STATE_SERIALISED) - g_buffer_unref (value->contents.serialised.buffer); + g_bytes_unref (value->contents.serialised.bytes); else g_variant_release_children (value); @@ -963,8 +963,8 @@ g_variant_get_child_value (GVariant *value, STATE_SERIALISED; child->size = s_child.size; child->ref_count = 1; - child->contents.serialised.buffer = - g_buffer_ref (value->contents.serialised.buffer); + child->contents.serialised.bytes = + g_bytes_ref (value->contents.serialised.bytes); child->contents.serialised.data = s_child.data; return child; diff --git a/glib/gvariant-core.h b/glib/gvariant-core.h index a31212e..1fd4829 100644 --- a/glib/gvariant-core.h +++ b/glib/gvariant-core.h @@ -23,12 +23,12 @@ #include #include -#include +#include /* gvariant-core.c */ G_GNUC_INTERNAL -GVariant * g_variant_new_from_buffer (const GVariantType *type, - GBuffer *buffer, +GVariant * g_variant_new_from_bytes (const GVariantType *type, + GBytes *bytes, gboolean trusted); G_GNUC_INTERNAL diff --git a/glib/gvariant.c b/glib/gvariant.c index 6e15c41..c3d1947 100644 --- a/glib/gvariant.c +++ b/glib/gvariant.c @@ -321,11 +321,11 @@ g_variant_new_from_trusted (const GVariantType *type, gsize size) { GVariant *value; - GBuffer *buffer; + GBytes *bytes; - buffer = g_buffer_new_from_data (data, size); - value = g_variant_new_from_buffer (type, buffer, TRUE); - g_buffer_unref (buffer); + bytes = g_bytes_new (data, size); + value = g_variant_new_from_bytes (type, bytes, TRUE); + g_bytes_unref (bytes); return value; } @@ -5098,7 +5098,7 @@ g_variant_byteswap (GVariant *value) { GVariantSerialised serialised; GVariant *trusted; - GBuffer *buffer; + GBytes *bytes; trusted = g_variant_get_normal_form (value); serialised.type_info = g_variant_get_type_info (trusted); @@ -5109,9 +5109,9 @@ g_variant_byteswap (GVariant *value) g_variant_serialised_byteswap (serialised); - buffer = g_buffer_new_take_data (serialised.data, serialised.size); - new = g_variant_new_from_buffer (g_variant_get_type (value), buffer, TRUE); - g_buffer_unref (buffer); + bytes = g_bytes_new (serialised.data, serialised.size); + new = g_variant_new_from_bytes (g_variant_get_type (value), bytes, TRUE); + g_bytes_unref (bytes); } else /* contains no multi-byte data */ @@ -5167,18 +5167,18 @@ g_variant_new_from_data (const GVariantType *type, gpointer user_data) { GVariant *value; - GBuffer *buffer; + GBytes *bytes; g_return_val_if_fail (g_variant_type_is_definite (type), NULL); g_return_val_if_fail (data != NULL || size == 0, NULL); if (notify) - buffer = g_buffer_new_from_pointer (data, size, notify, user_data); + bytes = g_bytes_new_with_free_func (data, size, notify, user_data); else - buffer = g_buffer_new_from_static_data (data, size); + bytes = g_bytes_new_static (data, size); - value = g_variant_new_from_buffer (type, buffer, trusted); - g_buffer_unref (buffer); + value = g_variant_new_from_bytes (type, bytes, trusted); + g_bytes_unref (bytes); return value; } diff --git a/glib/tests/.gitignore b/glib/tests/.gitignore index 49b3b83..1bc83c5 100644 --- a/glib/tests/.gitignore +++ b/glib/tests/.gitignore @@ -8,6 +8,7 @@ atomic base64 bitlock bookmarkfile +bytes cache checksum collate diff --git a/glib/tests/Makefile.am b/glib/tests/Makefile.am index 5d18189..3cfca22 100644 --- a/glib/tests/Makefile.am +++ b/glib/tests/Makefile.am @@ -67,6 +67,9 @@ markup_subparser_LDADD = $(progs_ldadd) TEST_PROGS += array-test array_test_LDADD = $(progs_ldadd) +TEST_PROGS += bytes +bytes_LDADD = $(progs_ldadd) + TEST_PROGS += hostutils hostutils_LDADD = $(progs_ldadd) diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c index 0e08cd1..de3a8ff 100644 --- a/glib/tests/array-test.c +++ b/glib/tests/array-test.c @@ -762,6 +762,37 @@ byte_array_sort_with_data (void) g_byte_array_free (gbarray, TRUE); } +static void +byte_array_new_take (void) +{ + GByteArray *gbarray; + guint8 *data; + + data = g_memdup ("woooweeewow", 11); + gbarray = g_byte_array_new_take (data, 11); + g_assert (gbarray->data == data); + g_assert_cmpuint (gbarray->len, ==, 11); + g_byte_array_free (gbarray, TRUE); +} + +static void +byte_array_free_to_bytes (void) +{ + GByteArray *gbarray; + gpointer memory; + GBytes *bytes; + + gbarray = g_byte_array_new (); + g_byte_array_append (gbarray, (guint8 *)"woooweeewow", 11); + memory = gbarray->data; + + bytes = g_byte_array_free_to_bytes (gbarray); + g_assert (bytes != NULL); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, 11); + g_assert (g_bytes_get_data (bytes) == memory); + + g_bytes_unref (bytes); +} int main (int argc, char *argv[]) { @@ -796,6 +827,8 @@ main (int argc, char *argv[]) g_test_add_func ("/bytearray/ref-count", byte_array_ref_count); g_test_add_func ("/bytearray/sort", byte_array_sort); g_test_add_func ("/bytearray/sort-with-data", byte_array_sort_with_data); + g_test_add_func ("/bytearray/new-take", byte_array_new_take); + g_test_add_func ("/bytearray/free-to-bytes", byte_array_free_to_bytes); return g_test_run (); } diff --git a/glib/tests/bytes.c b/glib/tests/bytes.c new file mode 100644 index 0000000..104bdce --- /dev/null +++ b/glib/tests/bytes.c @@ -0,0 +1,331 @@ +/* + * Copyright 2011 Collabora Ltd. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * See the included COPYING file for more information. + */ + +#undef G_DISABLE_ASSERT +#undef G_LOG_DOMAIN + +#include +#include +#include +#include "glib.h" + +static const gchar *NYAN = "nyannyan"; +static const gsize N_NYAN = 8; + +static void +test_new (void) +{ + const gchar *data; + GBytes *bytes; + + data = "test"; + bytes = g_bytes_new (data, 4); + g_assert (bytes != NULL); + g_assert (g_bytes_get_data (bytes) != data); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4); + g_assert (memcmp (data, g_bytes_get_data (bytes), g_bytes_get_size (bytes)) == 0); + + g_bytes_unref (bytes); +} + +static void +test_new_take (void) +{ + gchar *data; + GBytes *bytes; + + data = g_strdup ("test"); + bytes = g_bytes_new_take (data, 4); + g_assert (bytes != NULL); + g_assert (g_bytes_get_data (bytes) == data); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4); + + g_bytes_unref (bytes); +} + +static void +test_new_static (void) +{ + const gchar *data; + GBytes *bytes; + + data = "test"; + bytes = g_bytes_new_static (data, 4); + g_assert (bytes != NULL); + g_assert (g_bytes_get_data (bytes) == data); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4); + + g_bytes_unref (bytes); +} + +static void +test_new_from_bytes (void) +{ + const gchar *data = "smile and wave"; + GBytes *bytes; + GBytes *sub; + + bytes = g_bytes_new (data, 14); + sub = g_bytes_new_from_bytes (bytes, 10, 4); + + g_assert (sub != NULL); + g_assert (g_bytes_get_data (sub) == ((gchar *)g_bytes_get_data (bytes)) + 10); + g_assert (g_bytes_get_size (sub) == 4); + g_bytes_unref (bytes); + + g_assert (memcmp (g_bytes_get_data (sub), "wave", 4) == 0); + g_bytes_unref (sub); +} + +static void +on_destroy_increment (gpointer data) +{ + gint *count = data; + g_assert (count != NULL); + (*count)++; +} + +static void +test_new_with_free_func (void) +{ + GBytes *bytes; + gchar *data; + gint count = 0; + + data = "test"; + bytes = g_bytes_new_with_free_func (data, 4, on_destroy_increment, &count); + g_assert (bytes != NULL); + g_assert_cmpint (count, ==, 0); + g_assert (g_bytes_get_data (bytes) == data); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, 4); + + g_bytes_unref (bytes); + g_assert_cmpuint (count, ==, 1); +} + +static void +test_hash (void) +{ + GBytes *bytes1; + GBytes *bytes2; + guint hash1; + guint hash2; + + bytes1 = g_bytes_new ("blah", 4); + bytes2 = g_bytes_new ("blah", 4); + + hash1 = g_bytes_hash (bytes1); + hash2 = g_bytes_hash (bytes2); + g_assert (hash1 == hash2); + + g_bytes_unref (bytes1); + g_bytes_unref (bytes2); +} + +static void +test_equal (void) +{ + GBytes *bytes; + GBytes *bytes2; + + bytes = g_bytes_new ("blah", 4); + + bytes2 = g_bytes_new ("blah", 4); + g_assert (g_bytes_equal (bytes, bytes2)); + g_assert (g_bytes_equal (bytes2, bytes)); + g_bytes_unref (bytes2); + + bytes2 = g_bytes_new ("bla", 3); + g_assert (!g_bytes_equal (bytes, bytes2)); + g_assert (!g_bytes_equal (bytes2, bytes)); + g_bytes_unref (bytes2); + + bytes2 = g_bytes_new ("true", 4); + g_assert (!g_bytes_equal (bytes, bytes2)); + g_assert (!g_bytes_equal (bytes2, bytes)); + g_bytes_unref (bytes2); + + g_bytes_unref (bytes); +} + +static void +test_compare (void) +{ + GBytes *bytes; + GBytes *bytes2; + + bytes = g_bytes_new ("blah", 4); + + bytes2 = g_bytes_new ("blah", 4); + g_assert_cmpint (g_bytes_compare (bytes, bytes2), ==, 0); + g_bytes_unref (bytes2); + + bytes2 = g_bytes_new ("bla", 3); + g_assert_cmpint (g_bytes_compare (bytes, bytes2), >, 0); + g_bytes_unref (bytes2); + + bytes2 = g_bytes_new ("abcd", 4); + g_assert_cmpint (g_bytes_compare (bytes, bytes2), >, 0); + g_bytes_unref (bytes2); + + bytes2 = g_bytes_new ("blahblah", 8); + g_assert_cmpint (g_bytes_compare (bytes, bytes2), <, 0); + g_bytes_unref (bytes2); + + bytes2 = g_bytes_new ("zyx", 3); + g_assert_cmpint (g_bytes_compare (bytes, bytes2), <, 0); + g_bytes_unref (bytes2); + + bytes2 = g_bytes_new ("zyxw", 4); + g_assert_cmpint (g_bytes_compare (bytes, bytes2), <, 0); + g_bytes_unref (bytes2); + + g_bytes_unref (bytes); +} + +static void +test_to_data_transferred (void) +{ + gconstpointer memory; + gpointer data; + gsize size; + GBytes *bytes; + + /* Memory transferred: one reference, and allocated with g_malloc */ + bytes = g_bytes_new (NYAN, N_NYAN); + memory = g_bytes_get_data (bytes); + data = g_bytes_unref_to_data (bytes, &size); + g_assert (data == memory); + g_assert_cmpuint (size, ==, N_NYAN); + g_assert (memcmp (data, NYAN, N_NYAN) == 0); + g_free (data); +} + +static void +test_to_data_two_refs (void) +{ + gconstpointer memory; + gpointer data; + gsize size; + GBytes *bytes; + + /* Memory copied: two references */ + bytes = g_bytes_new (NYAN, N_NYAN); + bytes = g_bytes_ref (bytes); + memory = g_bytes_get_data (bytes); + data = g_bytes_unref_to_data (bytes, &size); + g_assert (data != memory); + g_assert_cmpuint (size, ==, N_NYAN); + g_assert (memcmp (data, NYAN, N_NYAN) == 0); + g_free (data); + g_assert (g_bytes_get_data (bytes) == memory); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, N_NYAN); + g_bytes_unref (bytes); +} + +static void +test_to_data_non_malloc (void) +{ + gpointer data; + gsize size; + GBytes *bytes; + + /* Memory copied: non malloc memory */ + bytes = g_bytes_new_static (NYAN, N_NYAN); + g_assert (g_bytes_get_data (bytes) == NYAN); + data = g_bytes_unref_to_data (bytes, &size); + g_assert (data != (gpointer)NYAN); + g_assert_cmpuint (size, ==, N_NYAN); + g_assert (memcmp (data, NYAN, N_NYAN) == 0); + g_free (data); +} + +static void +test_to_array_transferred (void) +{ + gconstpointer memory; + GByteArray *array; + GBytes *bytes; + + /* Memory transferred: one reference, and allocated with g_malloc */ + bytes = g_bytes_new (NYAN, N_NYAN); + memory = g_bytes_get_data (bytes); + array = g_bytes_unref_to_array (bytes); + g_assert (array != NULL); + g_assert (array->data == memory); + g_assert_cmpuint (array->len, ==, N_NYAN); + g_assert (memcmp (array->data, NYAN, N_NYAN) == 0); + g_byte_array_unref (array); +} + +static void +test_to_array_two_refs (void) +{ + gconstpointer memory; + GByteArray *array; + GBytes *bytes; + + /* Memory copied: two references */ + bytes = g_bytes_new (NYAN, N_NYAN); + bytes = g_bytes_ref (bytes); + memory = g_bytes_get_data (bytes); + array = g_bytes_unref_to_array (bytes); + g_assert (array != NULL); + g_assert (array->data != memory); + g_assert_cmpuint (array->len, ==, N_NYAN); + g_assert (memcmp (array->data, NYAN, N_NYAN) == 0); + g_byte_array_unref (array); + g_assert (g_bytes_get_data (bytes) == memory); + g_assert_cmpuint (g_bytes_get_size (bytes), ==, N_NYAN); + g_bytes_unref (bytes); +} + +static void +test_to_array_non_malloc (void) +{ + GByteArray *array; + GBytes *bytes; + + /* Memory copied: non malloc memory */ + bytes = g_bytes_new_static (NYAN, N_NYAN); + g_assert (g_bytes_get_data (bytes) == NYAN); + array = g_bytes_unref_to_array (bytes); + g_assert (array != NULL); + g_assert (array->data != (gpointer)NYAN); + g_assert_cmpuint (array->len, ==, N_NYAN); + g_assert (memcmp (array->data, NYAN, N_NYAN) == 0); + g_byte_array_unref (array); +} + +int +main (int argc, char *argv[]) +{ + g_test_init (&argc, &argv, NULL); + + g_test_bug_base ("http://bugs.gnome.org/"); + + g_test_add_func ("/bytes/new", test_new); + g_test_add_func ("/bytes/new-take", test_new_take); + g_test_add_func ("/bytes/new-static", test_new_static); + g_test_add_func ("/bytes/new-with-free-func", test_new_with_free_func); + g_test_add_func ("/bytes/new-from-bytes", test_new_from_bytes); + g_test_add_func ("/bytes/hash", test_hash); + g_test_add_func ("/bytes/equal", test_equal); + g_test_add_func ("/bytes/compare", test_compare); + g_test_add_func ("/bytes/to-data/transfered", test_to_data_transferred); + g_test_add_func ("/bytes/to-data/two-refs", test_to_data_two_refs); + g_test_add_func ("/bytes/to-data/non-malloc", test_to_data_non_malloc); + g_test_add_func ("/bytes/to-array/transfered", test_to_array_transferred); + g_test_add_func ("/bytes/to-array/two-refs", test_to_array_two_refs); + g_test_add_func ("/bytes/to-array/non-malloc", test_to_array_non_malloc); + + return g_test_run (); +} diff --git a/gobject/gboxed.c b/gobject/gboxed.c index 65ea908..755955c 100644 --- a/gobject/gboxed.c +++ b/gobject/gboxed.c @@ -127,6 +127,7 @@ G_DEFINE_BOXED_TYPE (GHashTable, g_hash_table, g_hash_table_ref, g_hash_table_un G_DEFINE_BOXED_TYPE (GArray, g_array, g_array_ref, g_array_unref) G_DEFINE_BOXED_TYPE (GPtrArray, g_ptr_array,g_ptr_array_ref, g_ptr_array_unref) G_DEFINE_BOXED_TYPE (GByteArray, g_byte_array, g_byte_array_ref, g_byte_array_unref) +G_DEFINE_BOXED_TYPE (GBytes, g_bytes, g_bytes_ref, g_bytes_unref); #ifdef ENABLE_REGEX G_DEFINE_BOXED_TYPE (GRegex, g_regex, g_regex_ref, g_regex_unref) diff --git a/gobject/glib-types.h b/gobject/glib-types.h index 7bad68c..9ff9f60 100644 --- a/gobject/glib-types.h +++ b/gobject/glib-types.h @@ -133,6 +133,15 @@ typedef gsize GType; #define G_TYPE_PTR_ARRAY (g_ptr_array_get_type ()) /** + * G_TYPE_BYTES: + * + * The #GType for #GBytes. + * + * Since: 2.32 + */ +#define G_TYPE_BYTES (g_bytes_get_type ()) + +/** * G_TYPE_VARIANT_TYPE: * * The #GType for a boxed type holding a #GVariantType. @@ -225,6 +234,7 @@ GType g_hash_table_get_type (void) G_GNUC_CONST; GType g_array_get_type (void) G_GNUC_CONST; GType g_byte_array_get_type (void) G_GNUC_CONST; GType g_ptr_array_get_type (void) G_GNUC_CONST; +GType g_bytes_get_type (void) G_GNUC_CONST; GType g_variant_type_get_gtype (void) G_GNUC_CONST; GType g_regex_get_type (void) G_GNUC_CONST; GType g_match_info_get_type (void) G_GNUC_CONST; diff --git a/gobject/gobject.symbols b/gobject/gobject.symbols index fafa832..dda51d0 100644 --- a/gobject/gobject.symbols +++ b/gobject/gobject.symbols @@ -21,6 +21,7 @@ g_strv_get_type g_hash_table_get_type g_array_get_type g_byte_array_get_type +g_bytes_get_type g_error_get_type g_ptr_array_get_type g_regex_get_type -- 2.7.4