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.
36 #include "gstqueuearray.h"
47 gboolean struct_array;
51 * gst_queue_array_new_for_struct: (skip)
52 * @struct_size: Size of each element (e.g. structure) in the array
53 * @initial_size: Initial size of the new queue
55 * Allocates a new #GstQueueArray object for elements (e.g. structures)
56 * of size @struct_size, with an initial queue size of @initial_size.
58 * Returns: a new #GstQueueArray object
63 gst_queue_array_new_for_struct (gsize struct_size, guint initial_size)
67 g_return_val_if_fail (struct_size > 0, NULL);
69 array = g_slice_new (GstQueueArray);
70 array->elt_size = struct_size;
71 array->size = initial_size;
72 array->array = g_malloc0 (struct_size * initial_size);
76 array->struct_array = TRUE;
81 * gst_queue_array_new: (skip)
82 * @initial_size: Initial size of the new queue
84 * Allocates a new #GstQueueArray object with an initial
85 * queue size of @initial_size.
87 * Returns: a new #GstQueueArray object
92 gst_queue_array_new (guint initial_size)
96 array = gst_queue_array_new_for_struct (sizeof (gpointer), initial_size);
97 array->struct_array = FALSE;
102 * gst_queue_array_free: (skip)
103 * @array: a #GstQueueArray object
105 * Frees queue @array and all memory associated to it.
110 gst_queue_array_free (GstQueueArray * array)
112 g_return_if_fail (array != NULL);
113 g_free (array->array);
114 g_slice_free (GstQueueArray, array);
118 * gst_queue_array_pop_head_struct: (skip)
119 * @array: a #GstQueueArray object
121 * Returns the head of the queue @array and removes it from the queue.
123 * Returns: pointer to element or struct, or NULL if @array was empty. The
124 * data pointed to by the returned pointer stays valid only as long as
125 * the queue array is not modified further!
130 gst_queue_array_pop_head_struct (GstQueueArray * array)
133 g_return_val_if_fail (array != NULL, NULL);
135 if (G_UNLIKELY (array->length == 0))
138 p_struct = array->array + (array->elt_size * array->head);
141 array->head %= array->size;
148 * gst_queue_array_pop_head: (skip)
149 * @array: a #GstQueueArray object
151 * Returns and head of the queue @array and removes
154 * Returns: The head of the queue
159 gst_queue_array_pop_head (GstQueueArray * array)
162 g_return_val_if_fail (array != NULL, NULL);
165 if (G_UNLIKELY (array->length == 0))
168 ret = *(gpointer *) (array->array + (sizeof (gpointer) * array->head));
170 array->head %= array->size;
176 * gst_queue_array_peek_head_struct: (skip)
177 * @array: a #GstQueueArray object
179 * Returns the head of the queue @array without removing it from the queue.
181 * Returns: pointer to element or struct, or NULL if @array was empty. The
182 * data pointed to by the returned pointer stays valid only as long as
183 * the queue array is not modified further!
188 gst_queue_array_peek_head_struct (GstQueueArray * array)
190 g_return_val_if_fail (array != NULL, NULL);
192 if (G_UNLIKELY (array->length == 0))
195 return array->array + (array->elt_size * array->head);
199 * gst_queue_array_peek_head: (skip)
200 * @array: a #GstQueueArray object
202 * Returns the head of the queue @array and does not
203 * remove it from the queue.
205 * Returns: The head of the queue
210 gst_queue_array_peek_head (GstQueueArray * array)
212 g_return_val_if_fail (array != NULL, NULL);
214 if (G_UNLIKELY (array->length == 0))
217 return *(gpointer *) (array->array + (sizeof (gpointer) * array->head));
221 * gst_queue_array_peek_nth: (skip)
223 * Returns the item at @idx in @array, but does not remove it from the queue.
225 * Returns: The item, or %NULL if @idx was out of bounds
230 gst_queue_array_peek_nth (GstQueueArray * array, guint idx)
232 g_return_val_if_fail (array != NULL, NULL);
233 g_return_val_if_fail (idx < array->length, NULL);
235 idx = (array->head + idx) % array->size;
237 return *(gpointer *) (array->array + (sizeof (gpointer) * idx));
241 * gst_queue_array_peek_nth_struct: (skip)
243 * Returns the item at @idx in @array, but does not remove it from the queue.
245 * Returns: The item, or %NULL if @idx was out of bounds
250 gst_queue_array_peek_nth_struct (GstQueueArray * array, guint idx)
252 g_return_val_if_fail (array != NULL, NULL);
253 g_return_val_if_fail (idx < array->length, NULL);
255 idx = (array->head + idx) % array->size;
257 return array->array + (array->elt_size * idx);
261 gst_queue_array_do_expand (GstQueueArray * array)
263 guint elt_size = array->elt_size;
264 /* newsize is 50% bigger */
265 guint oldsize = array->size;
266 guint newsize = MAX ((3 * oldsize) / 2, oldsize + 1);
269 if (array->tail != 0) {
270 guint8 *array2 = g_malloc0 (elt_size * newsize);
271 guint t1 = array->head;
272 guint t2 = oldsize - array->head;
274 /* [0-----TAIL][HEAD------SIZE]
276 * We want to end up with
277 * [HEAD------------------TAIL][----FREEDATA------NEWSIZE]
279 * 1) move [HEAD-----SIZE] part to beginning of new array
280 * 2) move [0-------TAIL] part new array, after previous part
283 memcpy (array2, array->array + (elt_size * array->head), t2 * elt_size);
284 memcpy (array2 + t2 * elt_size, array->array, t1 * elt_size);
286 g_free (array->array);
287 array->array = array2;
290 /* Fast path, we just need to grow the array */
291 array->array = g_realloc (array->array, elt_size * newsize);
292 memset (array->array + elt_size * oldsize, 0,
293 elt_size * (newsize - oldsize));
295 array->tail = oldsize;
296 array->size = newsize;
300 * gst_queue_array_push_element_tail: (skip)
301 * @array: a #GstQueueArray object
302 * @p_struct: address of element or structure to push to the tail of the queue
304 * Pushes the element at address @p_struct to the tail of the queue @array
305 * (Copies the contents of a structure of the struct_size specified when
306 * creating the queue into the array).
311 gst_queue_array_push_tail_struct (GstQueueArray * array, gpointer p_struct)
315 g_return_if_fail (p_struct != NULL);
316 g_return_if_fail (array != NULL);
317 elt_size = array->elt_size;
319 /* Check if we need to make room */
320 if (G_UNLIKELY (array->length == array->size))
321 gst_queue_array_do_expand (array);
323 memcpy (array->array + elt_size * array->tail, p_struct, elt_size);
325 array->tail %= array->size;
330 * gst_queue_array_push_tail: (skip)
331 * @array: a #GstQueueArray object
332 * @data: object to push
334 * Pushes @data to the tail of the queue @array.
339 gst_queue_array_push_tail (GstQueueArray * array, gpointer data)
341 g_return_if_fail (array != NULL);
343 /* Check if we need to make room */
344 if (G_UNLIKELY (array->length == array->size))
345 gst_queue_array_do_expand (array);
347 *(gpointer *) (array->array + sizeof (gpointer) * array->tail) = data;
349 array->tail %= array->size;
354 * gst_queue_array_peek_tail: (skip)
355 * @array: a #GstQueueArray object
357 * Returns the tail of the queue @array, but does not remove it from the queue.
359 * Returns: The tail of the queue
364 gst_queue_array_peek_tail (GstQueueArray * array)
368 g_return_val_if_fail (array != NULL, NULL);
376 idx = (array->head + (len - 1)) % array->size;
378 return *(gpointer *) (array->array + (sizeof (gpointer) * idx));
382 * gst_queue_array_peek_tail_struct: (skip)
383 * @array: a #GstQueueArray object
385 * Returns the tail of the queue @array, but does not remove it from the queue.
387 * Returns: The tail of the queue
392 gst_queue_array_peek_tail_struct (GstQueueArray * array)
396 g_return_val_if_fail (array != NULL, NULL);
404 idx = (array->head + (len - 1)) % array->size;
406 return array->array + (array->elt_size * idx);
410 * gst_queue_array_pop_tail: (skip)
411 * @array: a #GstQueueArray object
413 * Returns the tail of the queue @array and removes
416 * Returns: The tail of the queue
421 gst_queue_array_pop_tail (GstQueueArray * array)
426 g_return_val_if_fail (array != NULL, NULL);
434 idx = (array->head + (len - 1)) % array->size;
436 ret = *(gpointer *) (array->array + (sizeof (gpointer) * idx));
445 * gst_queue_array_pop_tail_struct: (skip)
446 * @array: a #GstQueueArray object
448 * Returns the tail of the queue @array and removes
451 * Returns: The tail of the queue
456 gst_queue_array_pop_tail_struct (GstQueueArray * array)
461 g_return_val_if_fail (array != NULL, NULL);
469 idx = (array->head + (len - 1)) % array->size;
471 ret = array->array + (array->elt_size * idx);
480 * gst_queue_array_is_empty: (skip)
481 * @array: a #GstQueueArray object
483 * Checks if the queue @array is empty.
485 * Returns: %TRUE if the queue @array is empty
490 gst_queue_array_is_empty (GstQueueArray * array)
492 g_return_val_if_fail (array != NULL, FALSE);
493 return (array->length == 0);
498 * gst_queue_array_drop_struct: (skip)
499 * @array: a #GstQueueArray object
500 * @idx: index to drop
501 * @p_struct: address into which to store the data of the dropped structure, or NULL
503 * Drops the queue element at position @idx from queue @array and copies the
504 * data of the element or structure that was removed into @p_struct if
505 * @p_struct is set (not NULL).
507 * Returns: TRUE on success, or FALSE on error
512 gst_queue_array_drop_struct (GstQueueArray * array, guint idx,
515 int first_item_index, last_item_index;
518 g_return_val_if_fail (array != NULL, FALSE);
519 idx = (array->head + idx) % array->size;
521 g_return_val_if_fail (array->length > 0, FALSE);
522 g_return_val_if_fail (idx < array->size, FALSE);
524 elt_size = array->elt_size;
526 first_item_index = array->head;
528 /* tail points to the first free spot */
529 last_item_index = (array->tail - 1 + array->size) % array->size;
531 if (p_struct != NULL)
532 memcpy (p_struct, array->array + elt_size * idx, elt_size);
534 /* simple case idx == first item */
535 if (idx == first_item_index) {
536 /* move the head plus one */
538 array->head %= array->size;
543 /* simple case idx == last item */
544 if (idx == last_item_index) {
545 /* move tail minus one, potentially wrapping */
546 array->tail = (array->tail - 1 + array->size) % array->size;
551 /* non-wrapped case */
552 if (first_item_index < last_item_index) {
553 g_assert (first_item_index < idx && idx < last_item_index);
554 /* move everything beyond idx one step towards zero in array */
555 memmove (array->array + elt_size * idx,
556 array->array + elt_size * (idx + 1),
557 (last_item_index - idx) * elt_size);
558 /* tail might wrap, ie if tail == 0 (and last_item_index == size) */
559 array->tail = (array->tail - 1 + array->size) % array->size;
564 /* only wrapped cases left */
565 g_assert (first_item_index > last_item_index);
567 if (idx < last_item_index) {
568 /* idx is before last_item_index, move data towards zero */
569 memmove (array->array + elt_size * idx,
570 array->array + elt_size * (idx + 1),
571 (last_item_index - idx) * elt_size);
572 /* tail should not wrap in this case! */
573 g_assert (array->tail > 0);
579 if (idx > first_item_index) {
580 /* idx is after first_item_index, move data to higher indices */
581 memmove (array->array + elt_size * (first_item_index + 1),
582 array->array + elt_size * first_item_index,
583 (idx - first_item_index) * elt_size);
585 /* head should not wrap in this case! */
586 g_assert (array->head < array->size);
591 g_return_val_if_reached (FALSE);
595 * gst_queue_array_drop_element: (skip)
596 * @array: a #GstQueueArray object
597 * @idx: index to drop
599 * Drops the queue element at position @idx from queue @array.
601 * Returns: the dropped element
606 gst_queue_array_drop_element (GstQueueArray * array, guint idx)
610 if (!gst_queue_array_drop_struct (array, idx, &ptr))
617 * gst_queue_array_find: (skip)
618 * @array: a #GstQueueArray object
619 * @func: (allow-none): comparison function, or %NULL to find @data by value
620 * @data: data for comparison function
622 * Finds an element in the queue @array, either by comparing every element
623 * with @func or by looking up @data if no compare function @func is provided,
624 * and returning the index of the found element.
626 * Returns: Index of the found element or -1 if nothing was found.
631 gst_queue_array_find (GstQueueArray * array, GCompareFunc func, gpointer data)
637 /* For struct arrays we need to implement this differently so that
638 * the user gets a pointer to the element data not the dereferenced
641 g_return_val_if_fail (array != NULL, -1);
642 g_return_val_if_fail (array->struct_array == FALSE, -1);
644 elt_size = array->elt_size;
647 /* Scan from head to tail */
648 for (i = 0; i < array->length; i++) {
649 p_element = array->array + ((i + array->head) % array->size) * elt_size;
650 if (func (*(gpointer *) p_element, data) == 0)
654 for (i = 0; i < array->length; i++) {
655 p_element = array->array + ((i + array->head) % array->size) * elt_size;
656 if (*(gpointer *) p_element == data)
665 * gst_queue_array_get_length: (skip)
666 * @array: a #GstQueueArray object
668 * Returns the length of the queue @array
670 * Returns: the length of the queue @array.
675 gst_queue_array_get_length (GstQueueArray * array)
677 g_return_val_if_fail (array != NULL, 0);
678 return array->length;