From 402847c8878a6bf839facdf7a91f096769ebc609 Mon Sep 17 00:00:00 2001 From: David Zeuthen Date: Wed, 29 Apr 2009 11:15:20 -0400 Subject: [PATCH] =?UTF-8?q?Bug=20580450=20=E2=80=93=20Reference=20counting?= =?UTF-8?q?=20and=20boxed=20types=20for=20arrays?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add reference counting and boxed types for GArray, GByteArray and GPtrArray. Signed-off-by: Matthias Clasen --- docs/reference/glib/glib-sections.txt | 9 + docs/reference/glib/tmpl/arrays.sgml | 41 ++- docs/reference/glib/tmpl/arrays_byte.sgml | 23 +- docs/reference/glib/tmpl/arrays_pointer.sgml | 64 ++++- glib/garray.c | 253 ++++++++++++++++++- glib/garray.h | 10 + glib/glib.symbols | 9 + glib/tests/array-test.c | 172 +++++++++++++ gobject/gboxed.c | 33 +++ gobject/gboxed.h | 27 ++ gobject/gobject.symbols | 3 + 11 files changed, 616 insertions(+), 28 deletions(-) diff --git a/docs/reference/glib/glib-sections.txt b/docs/reference/glib/glib-sections.txt index 85335afd0..2f3d95461 100644 --- a/docs/reference/glib/glib-sections.txt +++ b/docs/reference/glib/glib-sections.txt @@ -2117,6 +2117,9 @@ g_string_chunk_free GArray g_array_new g_array_sized_new +g_array_ref +g_array_unref +g_array_get_element_size g_array_append_val g_array_append_vals g_array_prepend_val @@ -2139,6 +2142,10 @@ g_array_free GPtrArray g_ptr_array_new g_ptr_array_sized_new +g_ptr_array_new_with_free_func +g_ptr_array_set_free_func +g_ptr_array_ref +g_ptr_array_unref g_ptr_array_add g_ptr_array_remove g_ptr_array_remove_index @@ -2160,6 +2167,8 @@ g_ptr_array_foreach GByteArray g_byte_array_new g_byte_array_sized_new +g_byte_array_ref +g_byte_array_unref g_byte_array_append g_byte_array_prepend g_byte_array_remove_index diff --git a/docs/reference/glib/tmpl/arrays.sgml b/docs/reference/glib/tmpl/arrays.sgml index 34c5497de..890e15cc0 100644 --- a/docs/reference/glib/tmpl/arrays.sgml +++ b/docs/reference/glib/tmpl/arrays.sgml @@ -69,7 +69,7 @@ added to the #GArray. -Creates a new #GArray. +Creates a new #GArray with a reference count of 1. @zero_terminated: %TRUE if the array should have an extra element at the end @@ -83,9 +83,9 @@ when they are allocated. Creates a new #GArray with @reserved_size elements -preallocated. This avoids frequent reallocation, if you are going to -add many elements to the array. Note however that the size of the -array is still 0. +preallocated and a reference count of 1. This avoids frequent reallocation, +if you are going to add many elements to the array. Note however that the +size of the array is still 0. @zero_terminated: %TRUE if the array should have an extra element at the end with all bits cleared. @@ -95,6 +95,32 @@ array is still 0. @Returns: the new #GArray. + + + + + +@array: +@Returns: + + + + + + + +@array: + + + + + + + +@array: +@Returns: + + Adds the value on to the end of the array. @@ -292,8 +318,11 @@ If the array was created with @clear_ set to %TRUE, the new elements are set to Frees the memory allocated for the #GArray. If @free_segment is %TRUE it frees the memory block holding the elements -as well. Pass %FALSE if you want to free the #GArray wrapper but preserve -the underlying array for use elsewhere. +as well and also each element if @array has a @element_free_func set. +Pass %FALSE if you want to free the #GArray wrapper but preserve +the underlying array for use elsewhere. If the reference count of @array +is greater than one, the #GArray wrapper is preserved but the size of +@array will be set to zero. diff --git a/docs/reference/glib/tmpl/arrays_byte.sgml b/docs/reference/glib/tmpl/arrays_byte.sgml index eb3c400a4..b1c4e4fca 100644 --- a/docs/reference/glib/tmpl/arrays_byte.sgml +++ b/docs/reference/glib/tmpl/arrays_byte.sgml @@ -63,7 +63,7 @@ added to the #GByteArray. -Creates a new #GByteArray. +Creates a new #GByteArray with a reference count of 1. @Returns: the new #GByteArray. @@ -80,6 +80,23 @@ the array. Note however that the size of the array is still 0. @Returns: the new #GByteArray. + + + + + +@array: +@Returns: + + + + + + + +@array: + + Adds the given bytes to the end of the #GByteArray. @@ -181,7 +198,9 @@ Sets the size of the #GByteArray, expanding it if necessary. Frees the memory allocated by the #GByteArray. -If @free_segment is %TRUE it frees the actual byte data. +If @free_segment is %TRUE it frees the actual byte data. If the reference +count of @array is greater than one, the #GByteArray wrapper is preserved but +the size of @array will be set to zero. @array: a #GByteArray. diff --git a/docs/reference/glib/tmpl/arrays_pointer.sgml b/docs/reference/glib/tmpl/arrays_pointer.sgml index 15c5792e3..07c45694f 100644 --- a/docs/reference/glib/tmpl/arrays_pointer.sgml +++ b/docs/reference/glib/tmpl/arrays_pointer.sgml @@ -73,7 +73,7 @@ Contains the public fields of a pointer array. -Creates a new #GPtrArray. +Creates a new #GPtrArray with a reference count of 1. @Returns: the new #GPtrArray. @@ -82,15 +82,50 @@ Creates a new #GPtrArray. Creates a new #GPtrArray with @reserved_size pointers -preallocated. This avoids frequent reallocation, if you are going to -add many pointers to the array. Note however that the size of the -array is still 0. +preallocated and a reference count of 1. This avoids frequent reallocation, +if you are going to add many pointers to the array. Note however that the size +of the array is still 0. @reserved_size: number of pointers preallocated. @Returns: the new #GPtrArray. + + + + + +@element_free_func: +@Returns: + + + + + + + +@array: +@element_free_func: + + + + + + + +@array: +@Returns: + + + + + + + +@array: + + Adds a pointer to the end of the pointer array. @@ -105,6 +140,8 @@ The array will grow in size automatically if necessary. Removes the first occurrence of the given pointer from the pointer array. The following elements are moved down one place. +If @array has a non-%NULL #GDestroyNotify function it is called for +the removed element. It returns %TRUE if the pointer was removed, or %FALSE if the pointer @@ -121,6 +158,8 @@ in the array. Removes the pointer at the given index from the pointer array. The following elements are moved down one place. +If @array has a non-%NULL #GDestroyNotify function it is called for +the removed element. @array: a #GPtrArray. @@ -134,6 +173,8 @@ Removes the first occurrence of the given pointer from the pointer array. The last element in the array is used to fill in the space, so this function does not preserve the order of the array. But it is faster than g_ptr_array_remove(). +If @array has a non-%NULL #GDestroyNotify function it is called for +the removed element. It returns %TRUE if the pointer was removed, or %FALSE if the pointer @@ -151,6 +192,8 @@ Removes the pointer at the given index from the pointer array. The last element in the array is used to fill in the space, so this function does not preserve the order of the array. But it is faster than g_ptr_array_remove_index(). +If @array has a non-%NULL #GDestroyNotify function it is called for +the removed element. @array: a #GPtrArray. @@ -162,6 +205,8 @@ g_ptr_array_remove_index(). Removes the given number of pointers starting at the given index from a #GPtrArray. The following elements are moved to close the gap. +If @array has a non-%NULL #GDestroyNotify function it is called for +the removed elements. @array: a @GPtrArray. @@ -228,14 +273,17 @@ Returns the pointer at the given index of the pointer array. Frees the memory allocated for the #GPtrArray. -If @free_segment is %TRUE it frees the memory block holding the elements +If @free_seg is %TRUE it frees the memory block holding the elements as well. Pass %FALSE if you want to free the #GPtrArray wrapper but preserve -the underlying array for use elsewhere. +the underlying array for use elsewhere. If the reference count of @array +is greater than one, the #GPtrArray wrapper is preserved but the size of +@array will be set to zero. -If array contents point to dynamically-allocated memory, they should be freed -separately. +If array contents point to dynamically-allocated memory, they should +be freed separately if @free_seg is %TRUE and no #GDestroyNotify +function has been set for @array. diff --git a/glib/garray.c b/glib/garray.c index b05ded6b2..64f70e77a 100644 --- a/glib/garray.c +++ b/glib/garray.c @@ -55,6 +55,7 @@ struct _GRealArray guint elt_size; guint zero_terminated : 1; guint clear : 1; + volatile gint ref_count; }; #define g_array_elt_len(array,i) ((array)->elt_size * (i)) @@ -91,6 +92,7 @@ GArray* g_array_sized_new (gboolean zero_terminated, array->zero_terminated = (zero_terminated ? 1 : 0); array->clear = (clear ? 1 : 0); array->elt_size = elt_size; + array->ref_count = 1; if (array->zero_terminated || reserved_size != 0) { @@ -101,14 +103,78 @@ GArray* g_array_sized_new (gboolean zero_terminated, return (GArray*) array; } +/** + * g_array_ref: + * @array: A #GArray. + * + * Atomically increments the reference count of @array by one. This + * function is MT-safe and may be called from any thread. + * + * Returns: The passed in #GArray. + * + * Since: 2.22 + **/ +GArray * +g_array_ref (GArray *array) +{ + GRealArray *rarray = (GRealArray*) array; + g_return_val_if_fail (g_atomic_int_get (&rarray->ref_count) > 0, array); + g_atomic_int_inc (&rarray->ref_count); + return array; +} + +/** + * g_array_unref: + * @array: A #GArray. + * + * Atomically decrements the reference count of @array by one. If the + * reference count drops to 0, all memory allocated by the array is + * released. This function is MT-safe and may be called from any + * thread. + * + * Since: 2.22 + **/ +void +g_array_unref (GArray *array) +{ + GRealArray *rarray = (GRealArray*) array; + g_return_if_fail (g_atomic_int_get (&rarray->ref_count) > 0); + if (g_atomic_int_dec_and_test (&rarray->ref_count)) + g_array_free (array, TRUE); +} + +/** + * g_array_get_element_size: + * @array: A #GArray. + * + * Gets the size of the elements in @array. + * + * Returns: Size of each element, in bytes. + * + * Since: 2.22 + **/ +guint +g_array_get_element_size (GArray *array) +{ + GRealArray *rarray = (GRealArray*) array; + return rarray->elt_size; +} + gchar* -g_array_free (GArray *array, +g_array_free (GArray *farray, gboolean free_segment) { + GRealArray *array = (GRealArray*) farray; gchar* segment; + gboolean preserve_wrapper; g_return_val_if_fail (array, NULL); + /* if others are holding a reference, preserve the wrapper but do free/return the data */ + preserve_wrapper = FALSE; + if (g_atomic_int_get (&array->ref_count) > 1) + preserve_wrapper = TRUE; + if (free_segment) { g_free (array->data); @@ -117,7 +183,16 @@ g_array_free (GArray *array, else segment = array->data; - g_slice_free1 (sizeof (GRealArray), array); + if (preserve_wrapper) + { + array->data = NULL; + array->len = 0; + array->alloc = 0; + } + else + { + g_slice_free1 (sizeof (GRealArray), array); + } return segment; } @@ -352,9 +427,11 @@ typedef struct _GRealPtrArray GRealPtrArray; struct _GRealPtrArray { - gpointer *pdata; - guint len; - guint alloc; + gpointer *pdata; + guint len; + guint alloc; + volatile gint ref_count; + GDestroyNotify element_free_func; }; static void g_ptr_array_maybe_expand (GRealPtrArray *array, @@ -374,6 +451,8 @@ g_ptr_array_sized_new (guint reserved_size) array->pdata = NULL; array->len = 0; array->alloc = 0; + array->ref_count = 1; + array->element_free_func = NULL; if (reserved_size != 0) g_ptr_array_maybe_expand (array, reserved_size); @@ -381,23 +460,123 @@ g_ptr_array_sized_new (guint reserved_size) return (GPtrArray*) array; } +/** + * g_ptr_array_new_with_free_func: + * @element_free_func: A function to free elements with destroy @array or %NULL. + * + * Creates a new #GPtrArray with a reference count of 1 and use @element_free_func + * for freeing each element when the array is destroyed either via + * g_ptr_array_unref(), when g_ptr_array_free() is called with @free_segment + * set to %TRUE or when removing elements. + * + * Returns: A new #GPtrArray. + * + * Since: 2.22 + **/ +GPtrArray * +g_ptr_array_new_with_free_func (GDestroyNotify element_free_func) +{ + GPtrArray *array; + + array = g_ptr_array_new (); + g_ptr_array_set_free_func (array, element_free_func); + return array; +} + +/** + * g_ptr_array_set_free_func: + * @array: A #GPtrArray. + * @element_free_func: A function to free elements with destroy @array or %NULL. + * + * Sets a function for freeing each element when @array is destroyed + * either via g_ptr_array_unref(), when g_ptr_array_free() is called + * with @free_segment set to %TRUE or when removing elements. + * + * Since: 2.22 + **/ +void +g_ptr_array_set_free_func (GPtrArray *array, + GDestroyNotify element_free_func) +{ + GRealPtrArray* rarray = (GRealPtrArray*) array; + rarray->element_free_func = element_free_func; +} + +/** + * g_ptr_array_ref: + * @array: A #GArray. + * + * Atomically increments the reference count of @array by one. This + * function is MT-safe and may be called from any thread. + * + * Returns: The passed in #GPtrArray. + * + * Since: 2.22 + **/ +GPtrArray * +g_ptr_array_ref (GPtrArray *array) +{ + GRealPtrArray *rarray = (GRealPtrArray*) array; + g_return_val_if_fail (g_atomic_int_get (&rarray->ref_count) > 0, array); + g_atomic_int_inc (&rarray->ref_count); + return array; +} + +/** + * g_ptr_array_unref: + * @array: A #GPtrArray. + * + * Atomically decrements the reference count of @array by one. If the + * reference count drops to 0, the effect is the same as calling + * g_ptr_array_free() with @free_segment set to %TRUE. This function + * is MT-safe and may be called from any thread. + * + * Since: 2.22 + **/ +void +g_ptr_array_unref (GPtrArray *array) +{ + GRealPtrArray *rarray = (GRealPtrArray*) array; + g_return_if_fail (g_atomic_int_get (&rarray->ref_count) > 0); + if (g_atomic_int_dec_and_test (&rarray->ref_count)) + g_ptr_array_free (array, TRUE); +} + gpointer* -g_ptr_array_free (GPtrArray *array, +g_ptr_array_free (GPtrArray *farray, gboolean free_segment) { + GRealPtrArray *array = (GRealPtrArray*) farray; gpointer* segment; + gboolean preserve_wrapper; g_return_val_if_fail (array, NULL); + /* if others are holding a reference, preserve the wrapper but do free/return the data */ + preserve_wrapper = FALSE; + if (g_atomic_int_get (&array->ref_count) > 1) + preserve_wrapper = TRUE; + if (free_segment) { + if (array->element_free_func != NULL) + g_ptr_array_foreach (farray, (GFunc) array->element_free_func, NULL); g_free (array->pdata); segment = NULL; } else segment = array->pdata; - g_slice_free1 (sizeof (GRealPtrArray), array); + if (preserve_wrapper) + { + array->pdata = NULL; + array->len = 0; + array->alloc = 0; + } + else + { + g_slice_free1 (sizeof (GRealPtrArray), array); + } return segment; } @@ -462,9 +641,12 @@ g_ptr_array_remove_index (GPtrArray *farray, result = array->pdata[index_]; + if (array->element_free_func != NULL) + array->element_free_func (array->pdata[index_]); + if (index_ != array->len - 1) g_memmove (array->pdata + index_, array->pdata + index_ + 1, - sizeof (gpointer) * (array->len - index_ - 1)); + sizeof (gpointer) * (array->len - index_ - 1)); array->len -= 1; @@ -488,7 +670,11 @@ g_ptr_array_remove_index_fast (GPtrArray *farray, result = array->pdata[index_]; if (index_ != array->len - 1) - array->pdata[index_] = array->pdata[array->len - 1]; + { + if (array->element_free_func != NULL) + array->element_free_func (array->pdata[index_]); + array->pdata[index_] = array->pdata[array->len - 1]; + } array->len -= 1; @@ -504,15 +690,24 @@ g_ptr_array_remove_range (GPtrArray *farray, guint length) { GRealPtrArray* array = (GRealPtrArray*) farray; + guint n; g_return_if_fail (array); g_return_if_fail (index_ < array->len); g_return_if_fail (index_ + length <= array->len); + if (array->element_free_func != NULL) + { + for (n = index_; n < index_ + length; n++) + array->element_free_func (array->pdata[n]); + } + if (index_ + length != array->len) - g_memmove (&array->pdata[index_], - &array->pdata[index_ + length], - (array->len - (index_ + length)) * sizeof (gpointer)); + { + g_memmove (&array->pdata[index_], + &array->pdata[index_ + length], + (array->len - (index_ + length)) * sizeof (gpointer)); + } array->len -= length; if (G_UNLIKELY (g_mem_gc_friendly)) @@ -646,6 +841,40 @@ guint8* g_byte_array_free (GByteArray *array, return (guint8*) g_array_free ((GArray*) array, free_segment); } +/** + * g_byte_array_ref: + * @array: A #GByteArray. + * + * Atomically increments the reference count of @array by one. This + * function is MT-safe and may be called from any thread. + * + * Returns: The passed in #GByteArray. + * + * Since: 2.22 + **/ +GByteArray * +g_byte_array_ref (GByteArray *array) +{ + return (GByteArray *) g_array_ref ((GArray *) array); +} + +/** + * g_byte_array_unref: + * @array: A #GByteArray. + * + * Atomically decrements the reference count of @array by one. If the + * reference count drops to 0, all memory allocated by the array is + * released. This function is MT-safe and may be called from any + * thread. + * + * Since: 2.22 + **/ +void +g_byte_array_unref (GByteArray *array) +{ + g_array_unref ((GArray *) array); +} + GByteArray* g_byte_array_append (GByteArray *array, const guint8 *data, guint len) diff --git a/glib/garray.h b/glib/garray.h index db001370a..6bc51f797 100644 --- a/glib/garray.h +++ b/glib/garray.h @@ -76,6 +76,9 @@ GArray* g_array_sized_new (gboolean zero_terminated, guint reserved_size); gchar* g_array_free (GArray *array, gboolean free_segment); +GArray *g_array_ref (GArray *array); +void g_array_unref (GArray *array); +guint g_array_get_element_size (GArray *array); GArray* g_array_append_vals (GArray *array, gconstpointer data, guint len); @@ -107,9 +110,14 @@ void g_array_sort_with_data (GArray *array, */ #define g_ptr_array_index(array,index_) ((array)->pdata)[index_] GPtrArray* g_ptr_array_new (void); +GPtrArray* g_ptr_array_new_with_free_func (GDestroyNotify element_free_func); GPtrArray* g_ptr_array_sized_new (guint reserved_size); gpointer* g_ptr_array_free (GPtrArray *array, gboolean free_seg); +GPtrArray* g_ptr_array_ref (GPtrArray *array); +void g_ptr_array_unref (GPtrArray *array); +void g_ptr_array_set_free_func (GPtrArray *array, + GDestroyNotify element_free_func); void g_ptr_array_set_size (GPtrArray *array, gint length); gpointer g_ptr_array_remove_index (GPtrArray *array, @@ -143,6 +151,8 @@ GByteArray* g_byte_array_new (void); GByteArray* g_byte_array_sized_new (guint reserved_size); guint8* g_byte_array_free (GByteArray *array, gboolean free_segment); +GByteArray *g_byte_array_ref (GByteArray *array); +void g_byte_array_unref (GByteArray *array); GByteArray* g_byte_array_append (GByteArray *array, const guint8 *data, guint len); diff --git a/glib/glib.symbols b/glib/glib.symbols index ce6a148f3..f8410cab7 100644 --- a/glib/glib.symbols +++ b/glib/glib.symbols @@ -17,6 +17,9 @@ g_array_append_vals g_array_free g_array_insert_vals g_array_new +g_array_ref +g_array_unref +g_array_get_element_size g_array_prepend_vals g_array_remove_index g_array_remove_index_fast @@ -27,6 +30,8 @@ g_array_sort g_array_sort_with_data g_byte_array_append g_byte_array_free +g_byte_array_unref +g_byte_array_ref g_byte_array_new g_byte_array_prepend g_byte_array_remove_index @@ -39,7 +44,11 @@ g_byte_array_sort_with_data g_ptr_array_add g_ptr_array_foreach g_ptr_array_free +g_ptr_array_unref +g_ptr_array_ref g_ptr_array_new +g_ptr_array_new_with_free_func +g_ptr_array_set_free_func g_ptr_array_remove g_ptr_array_remove_fast g_ptr_array_remove_index diff --git a/glib/tests/array-test.c b/glib/tests/array-test.c index 6f83a2762..3572ba39a 100644 --- a/glib/tests/array-test.c +++ b/glib/tests/array-test.c @@ -72,6 +72,33 @@ array_prepend (void) g_array_free (garray, TRUE); } +static void +array_ref_count (void) +{ + GArray *garray; + GArray *garray2; + gint i; + + garray = g_array_new (FALSE, FALSE, sizeof (gint)); + g_assert_cmpint (g_array_get_element_size (garray), ==, sizeof (gint)); + for (i = 0; i < 100; i++) + g_array_prepend_val (garray, i); + + /* check we can ref, unref and still access the array */ + garray2 = g_array_ref (garray); + g_assert (garray == garray2); + g_array_unref (garray2); + for (i = 0; i < 100; i++) + g_assert_cmpint (g_array_index (garray, gint, i), ==, (100 - i - 1)); + + /* garray2 should be an empty valid GArray wrapper */ + garray2 = g_array_ref (garray); + g_array_free (garray, TRUE); + + g_assert_cmpint (garray2->len, ==, 0); + g_array_unref (garray2); +} + static void pointer_array_add (void) { @@ -92,6 +119,118 @@ pointer_array_add (void) g_ptr_array_free (gparray, TRUE); } +static void +pointer_array_ref_count (void) +{ + GPtrArray *gparray; + GPtrArray *gparray2; + gint i; + gint sum = 0; + + gparray = g_ptr_array_new (); + for (i = 0; i < 10000; i++) + g_ptr_array_add (gparray, GINT_TO_POINTER (i)); + + /* check we can ref, unref and still access the array */ + gparray2 = g_ptr_array_ref (gparray); + g_assert (gparray == gparray2); + g_ptr_array_unref (gparray2); + for (i = 0; i < 10000; i++) + g_assert (g_ptr_array_index (gparray, i) == GINT_TO_POINTER (i)); + + g_ptr_array_foreach (gparray, sum_up, &sum); + g_assert (sum == 49995000); + + /* gparray2 should be an empty valid GPtrArray wrapper */ + gparray2 = g_ptr_array_ref (gparray); + g_ptr_array_free (gparray, TRUE); + + g_assert_cmpint (gparray2->len, ==, 0); + g_ptr_array_unref (gparray2); +} + +static gint num_free_func_invocations = 0; + +static void +my_free_func (gpointer data) +{ + num_free_func_invocations++; + g_free (data); +} + +static void +pointer_array_free_func (void) +{ + GPtrArray *gparray; + GPtrArray *gparray2; + gchar **strv; + gchar *s; + + num_free_func_invocations = 0; + gparray = g_ptr_array_new_with_free_func (my_free_func); + g_ptr_array_unref (gparray); + g_assert_cmpint (num_free_func_invocations, ==, 0); + + gparray = g_ptr_array_new_with_free_func (my_free_func); + g_ptr_array_free (gparray, TRUE); + g_assert_cmpint (num_free_func_invocations, ==, 0); + + num_free_func_invocations = 0; + gparray = g_ptr_array_new_with_free_func (my_free_func); + g_ptr_array_add (gparray, g_strdup ("foo")); + g_ptr_array_add (gparray, g_strdup ("bar")); + g_ptr_array_add (gparray, g_strdup ("baz")); + g_ptr_array_remove_index (gparray, 0); + g_assert_cmpint (num_free_func_invocations, ==, 1); + s = g_strdup ("frob"); + g_ptr_array_add (gparray, s); + g_assert (g_ptr_array_remove (gparray, s)); + g_assert_cmpint (num_free_func_invocations, ==, 2); + g_ptr_array_ref (gparray); + g_ptr_array_unref (gparray); + g_assert_cmpint (num_free_func_invocations, ==, 2); + g_ptr_array_unref (gparray); + g_assert_cmpint (num_free_func_invocations, ==, 4); + + num_free_func_invocations = 0; + gparray = g_ptr_array_new_with_free_func (my_free_func); + g_ptr_array_add (gparray, g_strdup ("foo")); + g_ptr_array_add (gparray, g_strdup ("bar")); + g_ptr_array_add (gparray, g_strdup ("baz")); + g_ptr_array_add (gparray, NULL); + gparray2 = g_ptr_array_ref (gparray); + strv = (gchar **) g_ptr_array_free (gparray, FALSE); + g_assert_cmpint (num_free_func_invocations, ==, 0); + g_strfreev (strv); + g_ptr_array_unref (gparray2); + g_assert_cmpint (num_free_func_invocations, ==, 0); + + num_free_func_invocations = 0; + gparray = g_ptr_array_new_with_free_func (my_free_func); + g_ptr_array_add (gparray, g_strdup ("foo")); + g_ptr_array_add (gparray, g_strdup ("bar")); + g_ptr_array_add (gparray, g_strdup ("baz")); + g_ptr_array_unref (gparray); + g_assert_cmpint (num_free_func_invocations, ==, 3); + + num_free_func_invocations = 0; + gparray = g_ptr_array_new_with_free_func (my_free_func); + g_ptr_array_add (gparray, g_strdup ("foo")); + g_ptr_array_add (gparray, g_strdup ("bar")); + g_ptr_array_add (gparray, g_strdup ("baz")); + g_ptr_array_free (gparray, TRUE); + g_assert_cmpint (num_free_func_invocations, ==, 3); + + num_free_func_invocations = 0; + gparray = g_ptr_array_new_with_free_func (my_free_func); + g_ptr_array_add (gparray, "foo"); + g_ptr_array_add (gparray, "bar"); + g_ptr_array_add (gparray, "baz"); + g_ptr_array_set_free_func (gparray, NULL); + g_ptr_array_free (gparray, TRUE); + g_assert_cmpint (num_free_func_invocations, ==, 0); +} + static void byte_array_append (void) { @@ -113,6 +252,35 @@ byte_array_append (void) g_byte_array_free (gbarray, TRUE); } +static void +byte_array_ref_count (void) +{ + GByteArray *gbarray; + GByteArray *gbarray2; + gint i; + + gbarray = g_byte_array_new (); + for (i = 0; i < 10000; i++) + g_byte_array_append (gbarray, (guint8*) "abcd", 4); + + gbarray2 = g_byte_array_ref (gbarray); + g_assert (gbarray2 == gbarray); + g_byte_array_unref (gbarray2); + for (i = 0; i < 10000; i++) + { + g_assert (gbarray->data[4*i] == 'a'); + g_assert (gbarray->data[4*i+1] == 'b'); + g_assert (gbarray->data[4*i+2] == 'c'); + g_assert (gbarray->data[4*i+3] == 'd'); + } + + gbarray2 = g_byte_array_ref (gbarray); + g_assert (gbarray2 == gbarray); + g_byte_array_free (gbarray, TRUE); + g_assert_cmpint (gbarray2->len, ==, 0); + g_byte_array_unref (gbarray2); +} + int main (int argc, char *argv[]) { @@ -121,12 +289,16 @@ main (int argc, char *argv[]) /* array tests */ g_test_add_func ("/array/append", array_append); g_test_add_func ("/array/prepend", array_prepend); + g_test_add_func ("/array/ref-count", array_ref_count); /* pointer arrays */ g_test_add_func ("/pointerarray/add", pointer_array_add); + g_test_add_func ("/pointerarray/ref-count", pointer_array_ref_count); + g_test_add_func ("/pointerarray/free-func", pointer_array_free_func); /* byte arrays */ g_test_add_func ("/bytearray/append", byte_array_append); + g_test_add_func ("/bytearray/ref-count", byte_array_ref_count); return g_test_run (); } diff --git a/gobject/gboxed.c b/gobject/gboxed.c index 8cdcd1ec5..e3a61703b 100644 --- a/gobject/gboxed.c +++ b/gobject/gboxed.c @@ -274,6 +274,39 @@ g_regex_get_type (void) return type_id; } +GType +g_array_get_type (void) +{ + static GType type_id = 0; + if (!type_id) + type_id = g_boxed_type_register_static (g_intern_static_string ("GArray"), + (GBoxedCopyFunc) g_array_ref, + (GBoxedFreeFunc) g_array_unref); + return type_id; +} + +GType +g_ptr_array_get_type (void) +{ + static GType type_id = 0; + if (!type_id) + type_id = g_boxed_type_register_static (g_intern_static_string ("GPtrArray"), + (GBoxedCopyFunc) g_ptr_array_ref, + (GBoxedFreeFunc) g_ptr_array_unref); + return type_id; +} + +GType +g_byte_array_get_type (void) +{ + static GType type_id = 0; + if (!type_id) + type_id = g_boxed_type_register_static (g_intern_static_string ("GByteArray"), + (GBoxedCopyFunc) g_byte_array_ref, + (GBoxedFreeFunc) g_byte_array_unref); + return type_id; +} + static void boxed_proxy_value_init (GValue *value) { diff --git a/gobject/gboxed.h b/gobject/gboxed.h index 75a5193af..82e272276 100644 --- a/gobject/gboxed.h +++ b/gobject/gboxed.h @@ -162,6 +162,30 @@ GType g_boxed_type_register_static (const gchar *name, * Since: 2.14 */ #define G_TYPE_REGEX (g_regex_get_type ()) +/** + * G_TYPE_ARRAY: + * + * The #GType for a boxed type holding a #GArray reference. + * + * Since: 2.22 + */ +#define G_TYPE_ARRAY (g_array_get_type ()) +/** + * G_TYPE_BYTE_ARRAY: + * + * The #GType for a boxed type holding a #GByteArray reference. + * + * Since: 2.22 + */ +#define G_TYPE_BYTE_ARRAY (g_byte_array_get_type ()) +/** + * G_TYPE_PTR_ARRAY: + * + * The #GType for a boxed type holding a #GPtrArray reference. + * + * Since: 2.22 + */ +#define G_TYPE_PTR_ARRAY (g_ptr_array_get_type ()) void g_value_take_boxed (GValue *value, @@ -177,6 +201,9 @@ GType g_date_get_type (void) G_GNUC_CONST; GType g_strv_get_type (void) G_GNUC_CONST; GType g_gstring_get_type (void) G_GNUC_CONST; 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_regex_get_type (void) G_GNUC_CONST; /** diff --git a/gobject/gobject.symbols b/gobject/gobject.symbols index 7a98dc31d..a43dd083b 100644 --- a/gobject/gobject.symbols +++ b/gobject/gobject.symbols @@ -20,6 +20,9 @@ g_date_get_type G_GNUC_CONST g_gstring_get_type G_GNUC_CONST g_strv_get_type G_GNUC_CONST g_hash_table_get_type G_GNUC_CONST +g_array_get_type G_GNUC_CONST +g_byte_array_get_type G_GNUC_CONST +g_ptr_array_get_type G_GNUC_CONST g_regex_get_type G_GNUC_CONST g_closure_get_type G_GNUC_CONST g_value_get_type G_GNUC_CONST