2 * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
3 * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public
18 * License along with this library; if not, write to the
19 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 * SECTION:gstqueuearray
25 * @title: GstQueueArray
26 * @short_description: Array based queue object
28 * #GstQueueArray is an object that provides standard queue functionality
29 * based on an array instead of linked lists. This reduces the overhead
30 * caused by memory management by a large factor.
38 #include "gstqueuearray.h"
49 gboolean struct_array;
50 GDestroyNotify clear_func;
54 * gst_queue_array_new_for_struct: (skip)
55 * @struct_size: Size of each element (e.g. structure) in the array
56 * @initial_size: Initial size of the new queue
58 * Allocates a new #GstQueueArray object for elements (e.g. structures)
59 * of size @struct_size, with an initial queue size of @initial_size.
61 * Returns: a new #GstQueueArray object
66 gst_queue_array_new_for_struct (gsize struct_size, guint initial_size)
70 g_return_val_if_fail (struct_size > 0, NULL);
72 array = g_slice_new (GstQueueArray);
73 array->elt_size = struct_size;
74 array->size = initial_size;
75 array->array = g_malloc0 (struct_size * initial_size);
79 array->struct_array = TRUE;
80 array->clear_func = NULL;
85 * gst_queue_array_new: (skip)
86 * @initial_size: Initial size of the new queue
88 * Allocates a new #GstQueueArray object with an initial
89 * queue size of @initial_size.
91 * Returns: a new #GstQueueArray object
96 gst_queue_array_new (guint initial_size)
100 array = gst_queue_array_new_for_struct (sizeof (gpointer), initial_size);
101 array->struct_array = FALSE;
106 * gst_queue_array_free: (skip)
107 * @array: a #GstQueueArray object
109 * Frees queue @array and all memory associated to it.
114 gst_queue_array_free (GstQueueArray * array)
116 g_return_if_fail (array != NULL);
117 gst_queue_array_clear (array);
118 g_free (array->array);
119 g_slice_free (GstQueueArray, array);
123 * gst_queue_array_set_clear_func: (skip)
124 * @array: a #GstQueueArray object
125 * @clear_func: a function to clear an element of @array
127 * Sets a function to clear an element of @array.
129 * The @clear_func will be called when an element in the array
130 * data segment is removed and when the array is freed and data
131 * segment is deallocated as well. @clear_func will be passed a
132 * pointer to the element to clear, rather than the element itself.
134 * Note that in contrast with other uses of #GDestroyNotify
135 * functions, @clear_func is expected to clear the contents of
136 * the array element it is given, but not free the element itself.
141 gst_queue_array_set_clear_func (GstQueueArray * array,
142 GDestroyNotify clear_func)
144 g_return_if_fail (array != NULL);
145 array->clear_func = clear_func;
149 * gst_queue_array_clear: (skip)
150 * @array: a #GstQueueArray object
152 * Clears queue @array and frees all memory associated to it.
157 gst_queue_array_clear (GstQueueArray * array)
159 g_return_if_fail (array != NULL);
161 if (array->clear_func != NULL) {
164 for (i = 0; i < array->length; i++) {
165 pos = (i + array->head) % array->size;
166 if (array->struct_array)
167 array->clear_func (array->array + pos * array->elt_size);
169 array->clear_func (*(gpointer *) (array->array +
170 pos * array->elt_size));
180 * gst_queue_array_pop_head_struct: (skip)
181 * @array: a #GstQueueArray object
183 * Returns the head of the queue @array and removes it from the queue.
185 * Returns: pointer to element or struct, or NULL if @array was empty. The
186 * data pointed to by the returned pointer stays valid only as long as
187 * the queue array is not modified further!
192 gst_queue_array_pop_head_struct (GstQueueArray * array)
195 g_return_val_if_fail (array != NULL, NULL);
197 if (G_UNLIKELY (array->length == 0))
200 p_struct = array->array + (array->elt_size * array->head);
203 array->head %= array->size;
210 * gst_queue_array_pop_head: (skip)
211 * @array: a #GstQueueArray object
213 * Returns and head of the queue @array and removes
216 * Returns: The head of the queue
221 gst_queue_array_pop_head (GstQueueArray * array)
224 g_return_val_if_fail (array != NULL, NULL);
227 if (G_UNLIKELY (array->length == 0))
230 ret = *(gpointer *) (array->array + (sizeof (gpointer) * array->head));
232 array->head %= array->size;
238 * gst_queue_array_peek_head_struct: (skip)
239 * @array: a #GstQueueArray object
241 * Returns the head of the queue @array without removing it from the queue.
243 * Returns: pointer to element or struct, or NULL if @array was empty. The
244 * data pointed to by the returned pointer stays valid only as long as
245 * the queue array is not modified further!
250 gst_queue_array_peek_head_struct (GstQueueArray * array)
252 g_return_val_if_fail (array != NULL, NULL);
254 if (G_UNLIKELY (array->length == 0))
257 return array->array + (array->elt_size * array->head);
261 * gst_queue_array_peek_head: (skip)
262 * @array: a #GstQueueArray object
264 * Returns the head of the queue @array and does not
265 * remove it from the queue.
267 * Returns: The head of the queue
272 gst_queue_array_peek_head (GstQueueArray * array)
274 g_return_val_if_fail (array != NULL, NULL);
276 if (G_UNLIKELY (array->length == 0))
279 return *(gpointer *) (array->array + (sizeof (gpointer) * array->head));
283 * gst_queue_array_peek_nth: (skip)
285 * Returns the item at @idx in @array, but does not remove it from the queue.
287 * Returns: The item, or %NULL if @idx was out of bounds
292 gst_queue_array_peek_nth (GstQueueArray * array, guint idx)
294 g_return_val_if_fail (array != NULL, NULL);
295 g_return_val_if_fail (idx < array->length, NULL);
297 idx = (array->head + idx) % array->size;
299 return *(gpointer *) (array->array + (sizeof (gpointer) * idx));
303 * gst_queue_array_peek_nth_struct: (skip)
305 * Returns the item at @idx in @array, but does not remove it from the queue.
307 * Returns: The item, or %NULL if @idx was out of bounds
312 gst_queue_array_peek_nth_struct (GstQueueArray * array, guint idx)
314 g_return_val_if_fail (array != NULL, NULL);
315 g_return_val_if_fail (idx < array->length, NULL);
317 idx = (array->head + idx) % array->size;
319 return array->array + (array->elt_size * idx);
323 gst_queue_array_do_expand (GstQueueArray * array)
325 guint elt_size = array->elt_size;
326 /* newsize is 50% bigger */
327 guint oldsize = array->size;
328 guint newsize = MAX ((3 * oldsize) / 2, oldsize + 1);
331 if (array->tail != 0) {
332 guint8 *array2 = g_malloc0 (elt_size * newsize);
333 guint t1 = array->head;
334 guint t2 = oldsize - array->head;
336 /* [0-----TAIL][HEAD------SIZE]
338 * We want to end up with
339 * [HEAD------------------TAIL][----FREEDATA------NEWSIZE]
341 * 1) move [HEAD-----SIZE] part to beginning of new array
342 * 2) move [0-------TAIL] part new array, after previous part
345 memcpy (array2, array->array + (elt_size * array->head), t2 * elt_size);
346 memcpy (array2 + t2 * elt_size, array->array, t1 * elt_size);
348 g_free (array->array);
349 array->array = array2;
352 /* Fast path, we just need to grow the array */
353 array->array = g_realloc (array->array, elt_size * newsize);
354 memset (array->array + elt_size * oldsize, 0,
355 elt_size * (newsize - oldsize));
357 array->tail = oldsize;
358 array->size = newsize;
362 * gst_queue_array_push_element_tail: (skip)
363 * @array: a #GstQueueArray object
364 * @p_struct: address of element or structure to push to the tail of the queue
366 * Pushes the element at address @p_struct to the tail of the queue @array
367 * (Copies the contents of a structure of the struct_size specified when
368 * creating the queue into the array).
373 gst_queue_array_push_tail_struct (GstQueueArray * array, gpointer p_struct)
377 g_return_if_fail (p_struct != NULL);
378 g_return_if_fail (array != NULL);
379 elt_size = array->elt_size;
381 /* Check if we need to make room */
382 if (G_UNLIKELY (array->length == array->size))
383 gst_queue_array_do_expand (array);
385 memcpy (array->array + elt_size * array->tail, p_struct, elt_size);
387 array->tail %= array->size;
392 * gst_queue_array_push_tail: (skip)
393 * @array: a #GstQueueArray object
394 * @data: object to push
396 * Pushes @data to the tail of the queue @array.
401 gst_queue_array_push_tail (GstQueueArray * array, gpointer data)
403 g_return_if_fail (array != NULL);
405 /* Check if we need to make room */
406 if (G_UNLIKELY (array->length == array->size))
407 gst_queue_array_do_expand (array);
409 *(gpointer *) (array->array + sizeof (gpointer) * array->tail) = data;
411 array->tail %= array->size;
416 * gst_queue_array_peek_tail: (skip)
417 * @array: a #GstQueueArray object
419 * Returns the tail of the queue @array, but does not remove it from the queue.
421 * Returns: The tail of the queue
426 gst_queue_array_peek_tail (GstQueueArray * array)
430 g_return_val_if_fail (array != NULL, NULL);
438 idx = (array->head + (len - 1)) % array->size;
440 return *(gpointer *) (array->array + (sizeof (gpointer) * idx));
444 * gst_queue_array_peek_tail_struct: (skip)
445 * @array: a #GstQueueArray object
447 * Returns the tail of the queue @array, but does not remove it from the queue.
449 * Returns: The tail of the queue
454 gst_queue_array_peek_tail_struct (GstQueueArray * array)
458 g_return_val_if_fail (array != NULL, NULL);
466 idx = (array->head + (len - 1)) % array->size;
468 return array->array + (array->elt_size * idx);
472 * gst_queue_array_pop_tail: (skip)
473 * @array: a #GstQueueArray object
475 * Returns the tail of the queue @array and removes
478 * Returns: The tail of the queue
483 gst_queue_array_pop_tail (GstQueueArray * array)
488 g_return_val_if_fail (array != NULL, NULL);
496 idx = (array->head + (len - 1)) % array->size;
498 ret = *(gpointer *) (array->array + (sizeof (gpointer) * idx));
507 * gst_queue_array_pop_tail_struct: (skip)
508 * @array: a #GstQueueArray object
510 * Returns the tail of the queue @array and removes
513 * Returns: The tail of the queue
518 gst_queue_array_pop_tail_struct (GstQueueArray * array)
523 g_return_val_if_fail (array != NULL, NULL);
531 idx = (array->head + (len - 1)) % array->size;
533 ret = array->array + (array->elt_size * idx);
542 * gst_queue_array_is_empty: (skip)
543 * @array: a #GstQueueArray object
545 * Checks if the queue @array is empty.
547 * Returns: %TRUE if the queue @array is empty
552 gst_queue_array_is_empty (GstQueueArray * array)
554 g_return_val_if_fail (array != NULL, FALSE);
555 return (array->length == 0);
560 * gst_queue_array_drop_struct: (skip)
561 * @array: a #GstQueueArray object
562 * @idx: index to drop
563 * @p_struct: address into which to store the data of the dropped structure, or NULL
565 * Drops the queue element at position @idx from queue @array and copies the
566 * data of the element or structure that was removed into @p_struct if
567 * @p_struct is set (not NULL).
569 * Returns: TRUE on success, or FALSE on error
574 gst_queue_array_drop_struct (GstQueueArray * array, guint idx,
577 int first_item_index, last_item_index;
580 g_return_val_if_fail (array != NULL, FALSE);
581 idx = (array->head + idx) % array->size;
583 g_return_val_if_fail (array->length > 0, FALSE);
584 g_return_val_if_fail (idx < array->size, FALSE);
586 elt_size = array->elt_size;
588 first_item_index = array->head;
590 /* tail points to the first free spot */
591 last_item_index = (array->tail - 1 + array->size) % array->size;
593 if (p_struct != NULL)
594 memcpy (p_struct, array->array + elt_size * idx, elt_size);
596 /* simple case idx == first item */
597 if (idx == first_item_index) {
598 /* move the head plus one */
600 array->head %= array->size;
605 /* simple case idx == last item */
606 if (idx == last_item_index) {
607 /* move tail minus one, potentially wrapping */
608 array->tail = (array->tail - 1 + array->size) % array->size;
613 /* non-wrapped case */
614 if (first_item_index < last_item_index) {
615 g_assert (first_item_index < idx && idx < last_item_index);
616 /* move everything beyond idx one step towards zero in array */
617 memmove (array->array + elt_size * idx,
618 array->array + elt_size * (idx + 1),
619 (last_item_index - idx) * elt_size);
620 /* tail might wrap, ie if tail == 0 (and last_item_index == size) */
621 array->tail = (array->tail - 1 + array->size) % array->size;
626 /* only wrapped cases left */
627 g_assert (first_item_index > last_item_index);
629 if (idx < last_item_index) {
630 /* idx is before last_item_index, move data towards zero */
631 memmove (array->array + elt_size * idx,
632 array->array + elt_size * (idx + 1),
633 (last_item_index - idx) * elt_size);
634 /* tail should not wrap in this case! */
635 g_assert (array->tail > 0);
641 if (idx > first_item_index) {
642 /* idx is after first_item_index, move data to higher indices */
643 memmove (array->array + elt_size * (first_item_index + 1),
644 array->array + elt_size * first_item_index,
645 (idx - first_item_index) * elt_size);
647 /* head should not wrap in this case! */
648 g_assert (array->head < array->size);
653 g_return_val_if_reached (FALSE);
657 * gst_queue_array_drop_element: (skip)
658 * @array: a #GstQueueArray object
659 * @idx: index to drop
661 * Drops the queue element at position @idx from queue @array.
663 * Returns: the dropped element
668 gst_queue_array_drop_element (GstQueueArray * array, guint idx)
672 if (!gst_queue_array_drop_struct (array, idx, &ptr))
679 * gst_queue_array_find: (skip)
680 * @array: a #GstQueueArray object
681 * @func: (allow-none): comparison function, or %NULL to find @data by value
682 * @data: data for comparison function
684 * Finds an element in the queue @array, either by comparing every element
685 * with @func or by looking up @data if no compare function @func is provided,
686 * and returning the index of the found element.
688 * Returns: Index of the found element or -1 if nothing was found.
693 gst_queue_array_find (GstQueueArray * array, GCompareFunc func, gpointer data)
699 /* For struct arrays we need to implement this differently so that
700 * the user gets a pointer to the element data not the dereferenced
703 g_return_val_if_fail (array != NULL, -1);
704 g_return_val_if_fail (array->struct_array == FALSE, -1);
706 elt_size = array->elt_size;
709 /* Scan from head to tail */
710 for (i = 0; i < array->length; i++) {
711 p_element = array->array + ((i + array->head) % array->size) * elt_size;
712 if (func (*(gpointer *) p_element, data) == 0)
716 for (i = 0; i < array->length; i++) {
717 p_element = array->array + ((i + array->head) % array->size) * elt_size;
718 if (*(gpointer *) p_element == data)
727 * gst_queue_array_get_length: (skip)
728 * @array: a #GstQueueArray object
730 * Returns the length of the queue @array
732 * Returns: the length of the queue @array.
737 gst_queue_array_get_length (GstQueueArray * array)
739 g_return_val_if_fail (array != NULL, 0);
740 return array->length;