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);
164 if (G_UNLIKELY (array->length == 0))
167 ret = *(gpointer *) (array->array + (sizeof (gpointer) * array->head));
169 array->head %= array->size;
175 * gst_queue_array_peek_head_struct: (skip)
176 * @array: a #GstQueueArray object
178 * Returns the head of the queue @array without removing it from the queue.
180 * Returns: pointer to element or struct, or NULL if @array was empty. The
181 * data pointed to by the returned pointer stays valid only as long as
182 * the queue array is not modified further!
187 gst_queue_array_peek_head_struct (GstQueueArray * array)
189 g_return_val_if_fail (array != NULL, NULL);
191 if (G_UNLIKELY (array->length == 0))
194 return array->array + (array->elt_size * array->head);
198 * gst_queue_array_peek_head: (skip)
199 * @array: a #GstQueueArray object
201 * Returns the head of the queue @array and does not
202 * remove it from the queue.
204 * Returns: The head of the queue
209 gst_queue_array_peek_head (GstQueueArray * array)
211 g_return_val_if_fail (array != NULL, NULL);
213 if (G_UNLIKELY (array->length == 0))
216 return *(gpointer *) (array->array + (sizeof (gpointer) * array->head));
220 gst_queue_array_do_expand (GstQueueArray * array)
222 guint elt_size = array->elt_size;
223 /* newsize is 50% bigger */
224 guint oldsize = array->size;
225 guint newsize = MAX ((3 * oldsize) / 2, oldsize + 1);
228 if (array->tail != 0) {
229 guint8 *array2 = g_malloc0 (elt_size * newsize);
230 guint t1 = array->head;
231 guint t2 = oldsize - array->head;
233 /* [0-----TAIL][HEAD------SIZE]
235 * We want to end up with
236 * [HEAD------------------TAIL][----FREEDATA------NEWSIZE]
238 * 1) move [HEAD-----SIZE] part to beginning of new array
239 * 2) move [0-------TAIL] part new array, after previous part
242 memcpy (array2, array->array + (elt_size * array->head), t2 * elt_size);
243 memcpy (array2 + t2 * elt_size, array->array, t1 * elt_size);
245 g_free (array->array);
246 array->array = array2;
249 /* Fast path, we just need to grow the array */
250 array->array = g_realloc (array->array, elt_size * newsize);
251 memset (array->array + elt_size * oldsize, 0,
252 elt_size * (newsize - oldsize));
254 array->tail = oldsize;
255 array->size = newsize;
259 * gst_queue_array_push_element_tail: (skip)
260 * @array: a #GstQueueArray object
261 * @p_struct: address of element or structure to push to the tail of the queue
263 * Pushes the element at address @p_struct to the tail of the queue @array
264 * (Copies the contents of a structure of the struct_size specified when
265 * creating the queue into the array).
270 gst_queue_array_push_tail_struct (GstQueueArray * array, gpointer p_struct)
274 g_return_if_fail (p_struct != NULL);
275 g_return_if_fail (array != NULL);
276 elt_size = array->elt_size;
278 /* Check if we need to make room */
279 if (G_UNLIKELY (array->length == array->size))
280 gst_queue_array_do_expand (array);
282 memcpy (array->array + elt_size * array->tail, p_struct, elt_size);
284 array->tail %= array->size;
289 * gst_queue_array_push_tail: (skip)
290 * @array: a #GstQueueArray object
291 * @data: object to push
293 * Pushes @data to the tail of the queue @array.
298 gst_queue_array_push_tail (GstQueueArray * array, gpointer data)
300 g_return_if_fail (array != NULL);
301 /* Check if we need to make room */
302 if (G_UNLIKELY (array->length == array->size))
303 gst_queue_array_do_expand (array);
305 *(gpointer *) (array->array + sizeof (gpointer) * array->tail) = data;
307 array->tail %= array->size;
312 * gst_queue_array_is_empty: (skip)
313 * @array: a #GstQueueArray object
315 * Checks if the queue @array is empty.
317 * Returns: %TRUE if the queue @array is empty
322 gst_queue_array_is_empty (GstQueueArray * array)
324 g_return_val_if_fail (array != NULL, FALSE);
325 return (array->length == 0);
330 * gst_queue_array_drop_struct: (skip)
331 * @array: a #GstQueueArray object
332 * @idx: index to drop
333 * @p_struct: address into which to store the data of the dropped structure, or NULL
335 * Drops the queue element at position @idx from queue @array and copies the
336 * data of the element or structure that was removed into @p_struct if
337 * @p_struct is set (not NULL).
339 * Returns: TRUE on success, or FALSE on error
344 gst_queue_array_drop_struct (GstQueueArray * array, guint idx,
347 int first_item_index, last_item_index;
350 g_return_val_if_fail (array != NULL, FALSE);
351 g_return_val_if_fail (array->length > 0, FALSE);
352 g_return_val_if_fail (idx < array->size, FALSE);
354 elt_size = array->elt_size;
356 first_item_index = array->head;
358 /* tail points to the first free spot */
359 last_item_index = (array->tail - 1 + array->size) % array->size;
361 if (p_struct != NULL)
362 memcpy (p_struct, array->array + elt_size * idx, elt_size);
364 /* simple case idx == first item */
365 if (idx == first_item_index) {
366 /* move the head plus one */
368 array->head %= array->size;
373 /* simple case idx == last item */
374 if (idx == last_item_index) {
375 /* move tail minus one, potentially wrapping */
376 array->tail = (array->tail - 1 + array->size) % array->size;
381 /* non-wrapped case */
382 if (first_item_index < last_item_index) {
383 g_assert (first_item_index < idx && idx < last_item_index);
384 /* move everything beyond idx one step towards zero in array */
385 memmove (array->array + elt_size * idx,
386 array->array + elt_size * (idx + 1),
387 (last_item_index - idx) * elt_size);
388 /* tail might wrap, ie if tail == 0 (and last_item_index == size) */
389 array->tail = (array->tail - 1 + array->size) % array->size;
394 /* only wrapped cases left */
395 g_assert (first_item_index > last_item_index);
397 if (idx < last_item_index) {
398 /* idx is before last_item_index, move data towards zero */
399 memmove (array->array + elt_size * idx,
400 array->array + elt_size * (idx + 1),
401 (last_item_index - idx) * elt_size);
402 /* tail should not wrap in this case! */
403 g_assert (array->tail > 0);
409 if (idx > first_item_index) {
410 /* idx is after first_item_index, move data to higher indices */
411 memmove (array->array + elt_size * (first_item_index + 1),
412 array->array + elt_size * first_item_index,
413 (idx - first_item_index) * elt_size);
415 /* head should not wrap in this case! */
416 g_assert (array->head < array->size);
421 g_return_val_if_reached (FALSE);
425 * gst_queue_array_drop_element: (skip)
426 * @array: a #GstQueueArray object
427 * @idx: index to drop
429 * Drops the queue element at position @idx from queue @array.
431 * Returns: the dropped element
436 gst_queue_array_drop_element (GstQueueArray * array, guint idx)
440 if (!gst_queue_array_drop_struct (array, idx, &ptr))
447 * gst_queue_array_find: (skip)
448 * @array: a #GstQueueArray object
449 * @func: (allow-none): comparison function, or %NULL to find @data by value
450 * @data: data for comparison function
452 * Finds an element in the queue @array, either by comparing every element
453 * with @func or by looking up @data if no compare function @func is provided,
454 * and returning the index of the found element.
456 * Note that the index is not 0-based, but an internal index number with a
457 * random offset. The index can be used in connection with
458 * gst_queue_array_drop_element(). FIXME: return index 0-based and make
459 * gst_queue_array_drop_element() take a 0-based index.
461 * Returns: Index of the found element or -1 if nothing was found.
466 gst_queue_array_find (GstQueueArray * array, GCompareFunc func, gpointer data)
472 /* For struct arrays we need to implement this differently so that
473 * the user gets a pointer to the element data not the dereferenced
476 g_return_val_if_fail (array != NULL, -1);
477 g_return_val_if_fail (array->struct_array == FALSE, -1);
479 elt_size = array->elt_size;
482 /* Scan from head to tail */
483 for (i = 0; i < array->length; i++) {
484 p_element = array->array + ((i + array->head) % array->size) * elt_size;
485 if (func (*(gpointer *) p_element, data) == 0)
486 return (i + array->head) % array->size;
489 for (i = 0; i < array->length; i++) {
490 p_element = array->array + ((i + array->head) % array->size) * elt_size;
491 if (*(gpointer *) p_element == data)
492 return (i + array->head) % array->size;
500 * gst_queue_array_get_length: (skip)
501 * @array: a #GstQueueArray object
503 * Returns the length of the queue @array
505 * Returns: the length of the queue @array.
510 gst_queue_array_get_length (GstQueueArray * array)
512 g_return_val_if_fail (array != NULL, 0);
513 return array->length;