diff --git a/girs/GstBase-1.0.gir b/girs/GstBase-1.0.gir
index 227002dc89..a9c05a41fb 100644
--- a/girs/GstBase-1.0.gir
+++ b/girs/GstBase-1.0.gir
@@ -13322,6 +13322,72 @@ it from the queue.
+
+ 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.
+
+
+
+
+
+
+ a #GstQueueArray object
+
+
+
+ object to push
+
+
+
+ comparison function
+
+
+
+ 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.
+
+
+
+
+
+
+ a #GstQueueArray object
+
+
+
+ address of element or structure to push into the queue
+
+
+
+ comparison function
+
+
+
+ data for comparison function
+
+
+
+
Pushes @data to the tail of the queue @array.
@@ -13379,6 +13445,28 @@ the array element it is given, but not free the element itself.
+
+ Sorts the queue @array by comparing elements against each other using
+the provided @compare_func.
+
+
+
+
+
+
+ a #GstQueueArray object
+
+
+
+ comparison function
+
+
+
+ data for comparison function
+
+
+
+
Allocates a new #GstQueueArray object with an initial
queue size of @initial_size.
diff --git a/subprojects/gstreamer/libs/gst/base/gstqueuearray.c b/subprojects/gstreamer/libs/gst/base/gstqueuearray.c
index 88b9f0b7f9..cfd80e4734 100644
--- a/subprojects/gstreamer/libs/gst/base/gstqueuearray.c
+++ b/subprojects/gstreamer/libs/gst/base/gstqueuearray.c
@@ -37,6 +37,9 @@
#include
#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
diff --git a/subprojects/gstreamer/libs/gst/base/gstqueuearray.h b/subprojects/gstreamer/libs/gst/base/gstqueuearray.h
index 77edec0990..2a63e95cf3 100644
--- a/subprojects/gstreamer/libs/gst/base/gstqueuearray.h
+++ b/subprojects/gstreamer/libs/gst/base/gstqueuearray.h
@@ -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
diff --git a/subprojects/gstreamer/tests/check/libs/queuearray.c b/subprojects/gstreamer/tests/check/libs/queuearray.c
index acd8e5c9a5..ecce1ec366 100644
--- a/subprojects/gstreamer/tests/check/libs/queuearray.c
+++ b/subprojects/gstreamer/tests/check/libs/queuearray.c
@@ -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;
}