Commit 402847c8 authored by David Zeuthen's avatar David Zeuthen

Bug 580450 – Reference counting and boxed types for arrays

Add reference counting and boxed types for GArray, GByteArray and GPtrArray.
Signed-off-by: Matthias Clasen's avatarMatthias Clasen <mclasen@redhat.com>
parent d80e1210
......@@ -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
......
......@@ -69,7 +69,7 @@ added to the #GArray.
<!-- ##### FUNCTION g_array_new ##### -->
<para>
Creates a new #GArray.
Creates a new #GArray with a reference count of 1.
</para>
@zero_terminated: %TRUE if the array should have an extra element at the end
......@@ -83,9 +83,9 @@ when they are allocated.
<!-- ##### FUNCTION g_array_sized_new ##### -->
<para>
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.
</para>
@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.
<!-- ##### FUNCTION g_array_ref ##### -->
<para>
</para>
@array:
@Returns:
<!-- ##### FUNCTION g_array_unref ##### -->
<para>
</para>
@array:
<!-- ##### FUNCTION g_array_get_element_size ##### -->
<para>
</para>
@array:
@Returns:
<!-- ##### MACRO g_array_append_val ##### -->
<para>
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
<para>
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.
</para>
<note>
<para>
......
......@@ -63,7 +63,7 @@ added to the #GByteArray.
<!-- ##### FUNCTION g_byte_array_new ##### -->
<para>
Creates a new #GByteArray.
Creates a new #GByteArray with a reference count of 1.
</para>
@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.
<!-- ##### FUNCTION g_byte_array_ref ##### -->
<para>
</para>
@array:
@Returns:
<!-- ##### FUNCTION g_byte_array_unref ##### -->
<para>
</para>
@array:
<!-- ##### FUNCTION g_byte_array_append ##### -->
<para>
Adds the given bytes to the end of the #GByteArray.
......@@ -181,7 +198,9 @@ Sets the size of the #GByteArray, expanding it if necessary.
<!-- ##### FUNCTION g_byte_array_free ##### -->
<para>
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.
</para>
@array: a #GByteArray.
......
......@@ -73,7 +73,7 @@ Contains the public fields of a pointer array.
<!-- ##### FUNCTION g_ptr_array_new ##### -->
<para>
Creates a new #GPtrArray.
Creates a new #GPtrArray with a reference count of 1.
</para>
@Returns: the new #GPtrArray.
......@@ -82,15 +82,50 @@ Creates a new #GPtrArray.
<!-- ##### FUNCTION g_ptr_array_sized_new ##### -->
<para>
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.
</para>
@reserved_size: number of pointers preallocated.
@Returns: the new #GPtrArray.
<!-- ##### FUNCTION g_ptr_array_new_with_free_func ##### -->
<para>
</para>
@element_free_func:
@Returns:
<!-- ##### FUNCTION g_ptr_array_set_free_func ##### -->
<para>
</para>
@array:
@element_free_func:
<!-- ##### FUNCTION g_ptr_array_ref ##### -->
<para>
</para>
@array:
@Returns:
<!-- ##### FUNCTION g_ptr_array_unref ##### -->
<para>
</para>
@array:
<!-- ##### FUNCTION g_ptr_array_add ##### -->
<para>
Adds a pointer to the end of the pointer array.
......@@ -105,6 +140,8 @@ The array will grow in size automatically if necessary.
<para>
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.
</para>
<para>
It returns %TRUE if the pointer was removed, or %FALSE if the pointer
......@@ -121,6 +158,8 @@ in the array.
<para>
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.
</para>
@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.
</para>
<para>
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.
</para>
@array: a #GPtrArray.
......@@ -162,6 +205,8 @@ g_ptr_array_remove_index().
<para>
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.
</para>
@array: a @GPtrArray.
......@@ -228,14 +273,17 @@ Returns the pointer at the given index of the pointer array.
<!-- ##### FUNCTION g_ptr_array_free ##### -->
<para>
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.
</para>
<note>
<para>
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.
</para>
</note>
......
......@@ -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)
......
......@@ -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);
......
......@@ -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
......
......@@ -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)
{