queuearray: Add sorting and sorted pushing

Adds gst_queue_array_sort for sorting and gst_queue_array_push_sorted{,struct} for pushing in a sorted order.
All three functions accept a comparison GCompareDataFunc along with optional user_data to pass to it.

In gst_queue_array_sort a small workaround was needed to correctly sort non-struct arrays. Like what _find() already
does, we need to dereference our pointers first, to make sure we can use the same comparison functions everywhere.
This is done via a small wrapper around the provided comparison function.
The array can also wrap around (tail ends up 'before' the head), in which case we have to reorder the array (similar to
what do_expand() does) to then be able to use an existing sorting function, like g_qsort_with_data().

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/5112>
This commit is contained in:
Piotr Brzeziński 2023-07-27 13:32:48 +02:00 committed by GStreamer Marge Bot
parent 314ffa3fb5
commit 601e31fe6e
4 changed files with 599 additions and 5 deletions

View file

@ -13322,6 +13322,72 @@ it from the queue.</doc>
</instance-parameter>
</parameters>
</method>
<method name="push_sorted" c:identifier="gst_queue_array_push_sorted" version="1.24" introspectable="0">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">Pushes @data to the queue @array, finding the correct position
by comparing @data with each array element using @func.
This has a time complexity of O(n), so depending on the size of the queue
and expected access patterns, a different data structure might be better.
Assumes that the array is already sorted. If it is not, make sure
to call gst_queue_array_sort() first.</doc>
<source-position filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.h"/>
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
<parameters>
<instance-parameter name="array" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">a #GstQueueArray object</doc>
<type name="QueueArray" c:type="GstQueueArray*"/>
</instance-parameter>
<parameter name="data" transfer-ownership="none" nullable="1" allow-none="1">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">object to push</doc>
<type name="gpointer" c:type="gpointer"/>
</parameter>
<parameter name="func" transfer-ownership="none" closure="2">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">comparison function</doc>
<type name="GLib.CompareDataFunc" c:type="GCompareDataFunc"/>
</parameter>
<parameter name="user_data" transfer-ownership="none" nullable="1" allow-none="1">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">data for comparison function</doc>
<type name="gpointer" c:type="gpointer"/>
</parameter>
</parameters>
</method>
<method name="push_sorted_struct" c:identifier="gst_queue_array_push_sorted_struct" version="1.24" introspectable="0">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">Pushes the element at address @p_struct into the queue @array
(copying the contents of a structure of the struct_size specified
when creating the queue into the array), finding the correct position
by comparing the element at @p_struct with each element in the array using @func.
This has a time complexity of O(n), so depending on the size of the queue
and expected access patterns, a different data structure might be better.
Assumes that the array is already sorted. If it is not, make sure
to call gst_queue_array_sort() first.</doc>
<source-position filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.h"/>
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
<parameters>
<instance-parameter name="array" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">a #GstQueueArray object</doc>
<type name="QueueArray" c:type="GstQueueArray*"/>
</instance-parameter>
<parameter name="p_struct" transfer-ownership="none" nullable="1" allow-none="1">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">address of element or structure to push into the queue</doc>
<type name="gpointer" c:type="gpointer"/>
</parameter>
<parameter name="func" transfer-ownership="none" closure="2">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">comparison function</doc>
<type name="GLib.CompareDataFunc" c:type="GCompareDataFunc"/>
</parameter>
<parameter name="user_data" transfer-ownership="none" nullable="1" allow-none="1">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">data for comparison function</doc>
<type name="gpointer" c:type="gpointer"/>
</parameter>
</parameters>
</method>
<method name="push_tail" c:identifier="gst_queue_array_push_tail" version="1.2" introspectable="0">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">Pushes @data to the tail of the queue @array.</doc>
<source-position filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.h"/>
@ -13379,6 +13445,28 @@ the array element it is given, but not free the element itself.</doc>
</parameter>
</parameters>
</method>
<method name="sort" c:identifier="gst_queue_array_sort" version="1.24" introspectable="0">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">Sorts the queue @array by comparing elements against each other using
the provided @compare_func.</doc>
<source-position filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.h"/>
<return-value transfer-ownership="none">
<type name="none" c:type="void"/>
</return-value>
<parameters>
<instance-parameter name="array" transfer-ownership="none">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">a #GstQueueArray object</doc>
<type name="QueueArray" c:type="GstQueueArray*"/>
</instance-parameter>
<parameter name="compare_func" transfer-ownership="none" closure="1">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">comparison function</doc>
<type name="GLib.CompareDataFunc" c:type="GCompareDataFunc"/>
</parameter>
<parameter name="user_data" transfer-ownership="none" nullable="1" allow-none="1">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">data for comparison function</doc>
<type name="gpointer" c:type="gpointer"/>
</parameter>
</parameters>
</method>
<function name="new" c:identifier="gst_queue_array_new" version="1.2" introspectable="0">
<doc xml:space="preserve" filename="../subprojects/gstreamer/libs/gst/base/gstqueuearray.c">Allocates a new #GstQueueArray object with an initial
queue size of @initial_size.</doc>

View file

@ -37,6 +37,9 @@
#include <gst/gst.h>
#include "gstqueuearray.h"
#define gst_queue_array_idx(a, i) \
((a)->array + (((a)->head + (i)) % (a)->size) * (a)->elt_size)
struct _GstQueueArray
{
/* < private > */
@ -50,6 +53,12 @@ struct _GstQueueArray
GDestroyNotify clear_func;
};
typedef struct
{
GCompareDataFunc func;
gpointer user_data;
} QueueSortData;
/**
* gst_queue_array_new_for_struct: (skip)
* @struct_size: Size of each element (e.g. structure) in the array
@ -431,6 +440,212 @@ gst_queue_array_push_tail (GstQueueArray * array, gpointer data)
array->length++;
}
/* Moves all elements in the queue placed after the given position in the internal array */
static void
gst_queue_array_move_data_after_position (GstQueueArray * array, guint pos)
{
guint elt_size = array->elt_size;
/* If the array does not wrap around OR if it does, but we're inserting past that point */
if (array->head < array->tail ||
(array->head >= array->tail && pos < array->head)) {
memmove (array->array + (pos + 1) * elt_size, array->array + pos * elt_size,
(array->tail - pos) * elt_size);
return;
}
/* Otherwise, array wraps around and we're inserting before the breaking point.
* First, move everything past that point by one place. */
memmove (array->array + elt_size, array->array, array->tail * elt_size);
/* Then move the last element from before the wrap-around point to right after it. */
memcpy (array->array, array->array + (array->size - 1) * elt_size, elt_size);
/* If we're inserting right before the breaking point, no further action is needed.
* Otherwise, move data between insertion point and the breaking point by one place. */
if (pos != array->size - 1) {
memmove (array->array + (pos + 1) * elt_size, array->array + pos * elt_size,
(array->size - pos - 1) * elt_size);
}
}
/**
* gst_queue_array_push_sorted: (skip)
* @array: a #GstQueueArray object
* @data: object to push
* @func: comparison function
* @user_data: (nullable): data for comparison function
*
* Pushes @data to the queue @array, finding the correct position
* by comparing @data with each array element using @func.
*
* This has a time complexity of O(n), so depending on the size of the queue
* and expected access patterns, a different data structure might be better.
*
* Assumes that the array is already sorted. If it is not, make sure
* to call gst_queue_array_sort() first.
*
* Since: 1.24
*/
void
gst_queue_array_push_sorted (GstQueueArray * array, gpointer data,
GCompareDataFunc func, gpointer user_data)
{
guint i;
gpointer *p_element;
g_return_if_fail (array != NULL);
g_return_if_fail (func != NULL);
/* Check if we need to make room */
if (G_UNLIKELY (array->length == array->size))
gst_queue_array_do_expand (array);
/* Compare against each element, assuming they're already sorted */
for (i = 0; i < array->length; i++) {
p_element = (gpointer *) gst_queue_array_idx (array, i);
if (func (*p_element, data, user_data) > 0) {
guint pos = (array->head + i) % array->size;
gst_queue_array_move_data_after_position (array, pos);
*p_element = data;
goto finish;
}
}
/* No 'bigger' element found - append to tail */
*(gpointer *) (array->array + array->elt_size * array->tail) = data;
finish:
array->tail++;
array->tail %= array->size;
array->length++;
}
/**
* gst_queue_array_push_sorted_struct: (skip)
* @array: a #GstQueueArray object
* @p_struct: address of element or structure to push into the queue
* @func: comparison function
* @user_data: (nullable): data for comparison function
*
* Pushes the element at address @p_struct into the queue @array
* (copying the contents of a structure of the struct_size specified
* when creating the queue into the array), finding the correct position
* by comparing the element at @p_struct with each element in the array using @func.
*
* This has a time complexity of O(n), so depending on the size of the queue
* and expected access patterns, a different data structure might be better.
*
* Assumes that the array is already sorted. If it is not, make sure
* to call gst_queue_array_sort() first.
*
* Since: 1.24
*/
void
gst_queue_array_push_sorted_struct (GstQueueArray * array, gpointer p_struct,
GCompareDataFunc func, gpointer user_data)
{
guint i;
gpointer p_element;
g_return_if_fail (array != NULL);
g_return_if_fail (p_struct != NULL);
g_return_if_fail (func != NULL);
/* Check if we need to make room */
if (G_UNLIKELY (array->length == array->size))
gst_queue_array_do_expand (array);
/* Compare against each element, assuming they're already sorted */
for (i = 0; i < array->length; i++) {
p_element = gst_queue_array_idx (array, i);
if (func (p_element, p_struct, user_data) > 0) {
guint pos = (array->head + i) % array->size;
gst_queue_array_move_data_after_position (array, pos);
memcpy (p_element, p_struct, array->elt_size);
goto finish;
}
}
/* No 'bigger' element found - append to tail */
memcpy (array->array + array->elt_size * array->tail, p_struct,
array->elt_size);
finish:
array->tail++;
array->tail %= array->size;
array->length++;
}
static int
compare_wrapper (gpointer * a, gpointer * b, QueueSortData * sort_data)
{
return sort_data->func (*a, *b, sort_data->user_data);
}
/**
* gst_queue_array_sort: (skip)
* @array: a #GstQueueArray object
* @compare_func: comparison function
* @user_data: (nullable): data for comparison function
*
* Sorts the queue @array by comparing elements against each other using
* the provided @compare_func.
*
* Since: 1.24
*/
void
gst_queue_array_sort (GstQueueArray * array, GCompareDataFunc compare_func,
gpointer user_data)
{
g_return_if_fail (array != NULL);
g_return_if_fail (compare_func != NULL);
if (array->length == 0)
return;
/* To be able to use g_qsort_with_data, we might need to rearrange:
* [0-----TAIL][HEAD-----SIZE] -> [HEAD-------TAIL] */
if (array->head >= array->tail) {
gsize t1 = array->head;
gsize t2 = array->size - array->head;
gsize elt_size = array->elt_size;
/* Copy [0-------TAIL] part to a temporary buffer */
guint8 *tmp = g_malloc0_n (t1, elt_size);
memcpy (tmp, array->array, t1 * elt_size);
/* Move [HEAD-----SIZE] part to the beginning of the original array */
memmove (array->array, array->array + (elt_size * array->head),
t2 * elt_size);
/* Copy the temporary buffer to the end of the original array */
memmove (array->array + (t2 * elt_size), tmp, t1 * elt_size);
g_free (tmp);
array->head = 0;
array->tail = array->length % array->size;
}
if (array->struct_array) {
g_qsort_with_data (array->array +
(array->head % array->size) * array->elt_size, array->length,
array->elt_size, compare_func, user_data);
} else {
/* For non-struct arrays, we need to wrap the provided compare function
* to dereference our pointers before passing them for comparison.
* This matches the behaviour of gst_queue_array_find(). */
QueueSortData sort_data = { compare_func, user_data };
g_qsort_with_data (array->array +
(array->head % array->size) * array->elt_size, array->length,
array->elt_size, (GCompareDataFunc) compare_wrapper, &sort_data);
}
}
/**
* gst_queue_array_peek_tail: (skip)
* @array: a #GstQueueArray object
@ -718,7 +933,7 @@ gst_queue_array_drop_element (GstQueueArray * array, guint idx)
/**
* gst_queue_array_find: (skip)
* @array: a #GstQueueArray object
* @func: (allow-none): comparison function, or %NULL to find @data by value
* @func: (nullable): comparison function, or %NULL to find @data by value
* @data: data for comparison function
*
* Finds an element in the queue @array, either by comparing every element

View file

@ -104,6 +104,23 @@ gpointer gst_queue_array_pop_tail_struct (GstQueueArray * array);
GST_BASE_API
gpointer gst_queue_array_peek_tail_struct (GstQueueArray * array);
GST_BASE_API
void gst_queue_array_push_sorted (GstQueueArray * array,
gpointer data,
GCompareDataFunc func,
gpointer user_data);
GST_BASE_API
void gst_queue_array_push_sorted_struct (GstQueueArray * array,
gpointer p_struct,
GCompareDataFunc func,
gpointer user_data);
GST_BASE_API
void gst_queue_array_sort (GstQueueArray *array,
GCompareDataFunc compare_func,
gpointer user_data);
G_END_DECLS
#endif

View file

@ -200,9 +200,9 @@ GST_START_TEST (test_array_grow_end)
GST_END_TEST;
static int
compare_pointer_value (gconstpointer a, gconstpointer b)
compare_pointer_value (guintptr a, guintptr b)
{
return (int) ((guintptr) a - (guintptr) b);
return (int) (a - b);
}
GST_START_TEST (test_array_drop2)
@ -232,8 +232,8 @@ GST_START_TEST (test_array_drop2)
gpointer dropped;
if (g_random_boolean () && g_random_boolean () && in_array[i]) {
idx = gst_queue_array_find (array, compare_pointer_value,
GUINT_TO_POINTER (i));
idx = gst_queue_array_find (array,
(GCompareFunc) compare_pointer_value, GUINT_TO_POINTER (i));
dropped = gst_queue_array_drop_element (array, idx);
fail_unless_equals_int (i, GPOINTER_TO_INT (dropped));
in_array[i] = FALSE;
@ -341,6 +341,273 @@ GST_START_TEST (test_array_peek_pop_tail)
GST_END_TEST;
GST_START_TEST (test_array_push_sorted)
{
GstQueueArray *array;
gint i;
/* Create an array of initial size 10 */
array = gst_queue_array_new (10);
/* Fill it with odd values */
for (i = 1; i < 10; i += 2)
gst_queue_array_push_tail (array, GINT_TO_POINTER (i));
/* Now try to push even values, in reverse order because why not */
for (i = 8; i >= 0; i -= 2)
gst_queue_array_push_sorted (array, GINT_TO_POINTER (i),
(GCompareDataFunc) compare_pointer_value, NULL);
fail_unless_equals_int (gst_queue_array_get_length (array), 10);
/* Check that the array is now 0-9 in correct order */
for (i = 0; i < 10; i++)
fail_unless_equals_int (GPOINTER_TO_INT (gst_queue_array_pop_head (array)),
i);
gst_queue_array_free (array);
}
GST_END_TEST;
GST_START_TEST (test_array_push_sorted_wrapped)
{
GstQueueArray *array;
gint i;
/* Create an array of initial size 10 */
array = gst_queue_array_new (10);
/* Push and pull 4 values to offset head/tail.
* Pushing +1's the tail and popping +1's the head, so the push after this will
* store data at [4] internally, and further 10 pushes will cause the array
* to wrap around. */
for (i = 0; i < 4; i++) {
gst_queue_array_push_tail (array, GINT_TO_POINTER (i));
fail_unless_equals_int (GPOINTER_TO_INT (gst_queue_array_pop_head (array)),
i);
}
/* Fill it with odd values */
for (i = 1; i < 10; i += 2)
gst_queue_array_push_tail (array, GINT_TO_POINTER (i));
/* Now try to push even values, in reverse order because why not */
for (i = 8; i >= 0; i -= 2)
gst_queue_array_push_sorted (array, GINT_TO_POINTER (i),
(GCompareDataFunc) compare_pointer_value, NULL);
fail_unless_equals_int (gst_queue_array_get_length (array), 10);
/* Check that the array is now 0-9 in correct order */
for (i = 0; i < 10; i++)
fail_unless_equals_int (GPOINTER_TO_INT (gst_queue_array_pop_head (array)),
i);
gst_queue_array_free (array);
}
GST_END_TEST;
typedef struct
{
gint value;
} CompareTestStruct;
static int
compare_struct_value (CompareTestStruct * a, CompareTestStruct * b)
{
return a->value - b->value;
}
GST_START_TEST (test_array_push_sorted_struct)
{
GstQueueArray *array;
gint i;
/* Create an array of initial size 10 */
array = gst_queue_array_new_for_struct (sizeof (CompareTestStruct), 10);
/* Fill it with odd values */
for (i = 1; i < 10; i += 2) {
CompareTestStruct s = { i };
gst_queue_array_push_tail_struct (array, &s);
}
/* Now try to push even values, in reverse order because why not */
for (i = 8; i >= 0; i -= 2) {
CompareTestStruct s = { i };
gst_queue_array_push_sorted_struct (array, &s,
(GCompareDataFunc) compare_struct_value, NULL);
}
fail_unless_equals_int (gst_queue_array_get_length (array), 10);
/* Check that the array is now 0-9 in correct order */
for (i = 0; i < 10; i++) {
CompareTestStruct *s = gst_queue_array_pop_head_struct (array);
fail_unless_equals_int (s->value, i);
}
gst_queue_array_free (array);
}
GST_END_TEST;
GST_START_TEST (test_array_push_sorted_struct_wrapped)
{
GstQueueArray *array;
gint i;
/* Create an array of initial size 10 */
array = gst_queue_array_new_for_struct (sizeof (CompareTestStruct), 10);
/* Push and pull 4 values to offset head/tail.
* Pushing +1's the tail and popping +1's the head, so the push after this will
* store data at [4] internally, and further 10 pushes will cause the array
* to wrap around. */
for (i = 0; i < 4; i++) {
gst_queue_array_push_tail (array, GINT_TO_POINTER (i));
fail_unless_equals_int (GPOINTER_TO_INT (gst_queue_array_pop_head (array)),
i);
}
/* Fill it with odd values */
for (i = 1; i < 10; i += 2) {
CompareTestStruct s = { i };
gst_queue_array_push_tail_struct (array, &s);
}
/* Now try to push even values, in reverse order because why not */
for (i = 8; i >= 0; i -= 2) {
CompareTestStruct s = { i };
gst_queue_array_push_sorted_struct (array, &s,
(GCompareDataFunc) compare_struct_value, NULL);
}
fail_unless_equals_int (gst_queue_array_get_length (array), 10);
/* Check that the array is now 0-9 in correct order */
for (i = 0; i < 10; i++) {
CompareTestStruct *s = gst_queue_array_pop_head_struct (array);
fail_unless_equals_int (s->value, i);
}
gst_queue_array_free (array);
}
GST_END_TEST;
GST_START_TEST (test_array_sort)
{
GstQueueArray *array;
gint i;
/* Create an array of initial size 10 */
array = gst_queue_array_new (10);
/* Fill it with odd values */
for (i = 1; i < 10; i += 2)
gst_queue_array_push_tail (array, GINT_TO_POINTER (i));
/* Now try to push even values, in reverse order because why not */
for (i = 8; i >= 0; i -= 2)
gst_queue_array_push_tail (array, GINT_TO_POINTER (i));
fail_unless_equals_int (gst_queue_array_get_length (array), 10);
/* Sort the array */
gst_queue_array_sort (array, (GCompareDataFunc) compare_pointer_value, NULL);
fail_unless_equals_int (gst_queue_array_get_length (array), 10);
/* Check that the array is now 0-9 in correct order */
for (i = 0; i < 10; i++)
fail_unless_equals_int (GPOINTER_TO_INT (gst_queue_array_pop_head (array)),
i);
gst_queue_array_free (array);
}
GST_END_TEST;
GST_START_TEST (test_array_sort_struct)
{
GstQueueArray *array;
gint i;
/* Create an array of initial size 10 */
array = gst_queue_array_new_for_struct (sizeof (CompareTestStruct), 10);
/* Fill it with odd values */
for (i = 1; i < 10; i += 2) {
CompareTestStruct s = { i };
gst_queue_array_push_tail_struct (array, &s);
}
/* Now try to push even values, in reverse order because why not */
for (i = 8; i >= 0; i -= 2) {
CompareTestStruct s = { i };
gst_queue_array_push_tail_struct (array, &s);
}
fail_unless_equals_int (gst_queue_array_get_length (array), 10);
/* Sort the array */
gst_queue_array_sort (array, (GCompareDataFunc) compare_struct_value, NULL);
/* Check that the array is now 0-9 in correct order */
for (i = 0; i < 10; i++) {
CompareTestStruct *s = gst_queue_array_pop_head_struct (array);
fail_unless_equals_int (s->value, i);
}
gst_queue_array_free (array);
}
GST_END_TEST;
GST_START_TEST (test_array_sort_wrapped)
{
GstQueueArray *array;
gint i;
/* Create an array of initial size 10 */
array = gst_queue_array_new (10);
/* Push and pull 4 values to offset head/tail */
for (i = 0; i < 4; i++) {
gst_queue_array_push_tail (array, GINT_TO_POINTER (i));
fail_unless_equals_int (GPOINTER_TO_INT (gst_queue_array_pop_head (array)),
i);
}
fail_unless_equals_int (gst_queue_array_get_length (array), 0);
/* Fill it with odd values */
for (i = 1; i < 10; i += 2)
gst_queue_array_push_tail (array, GINT_TO_POINTER (i));
/* Now try to push even values, in reverse order because why not
* At this point the array should've wrapped around (head > tail) */
for (i = 8; i >= 0; i -= 2)
gst_queue_array_push_tail (array, GINT_TO_POINTER (i));
fail_unless_equals_int (gst_queue_array_get_length (array), 10);
/* Sort the array */
gst_queue_array_sort (array, (GCompareDataFunc) compare_pointer_value, NULL);
/* Check that the array is now 0-9 in correct order */
for (i = 0; i < 10; i++)
fail_unless_equals_int (GPOINTER_TO_INT (gst_queue_array_pop_head (array)),
i);
gst_queue_array_free (array);
}
GST_END_TEST;
static Suite *
gst_queue_array_suite (void)
{
@ -358,6 +625,13 @@ gst_queue_array_suite (void)
tcase_add_test (tc_chain, test_array_grow_from_prealloc1);
tcase_add_test (tc_chain, test_array_peek_pop_tail);
tcase_add_test (tc_chain, test_array_peek_nth);
tcase_add_test (tc_chain, test_array_push_sorted);
tcase_add_test (tc_chain, test_array_push_sorted_wrapped);
tcase_add_test (tc_chain, test_array_push_sorted_struct);
tcase_add_test (tc_chain, test_array_push_sorted_struct_wrapped);
tcase_add_test (tc_chain, test_array_sort);
tcase_add_test (tc_chain, test_array_sort_struct);
tcase_add_test (tc_chain, test_array_sort_wrapped);
return s;
}