Port gtk-doc comments to their equivalent markdown syntax
[platform/upstream/gstreamer.git] / libs / gst / base / gstqueuearray.c
1 /* GStreamer
2  * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
3  * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
4  *
5  * gstqueuearray.c:
6  *
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.
11  *
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.
16  *
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.
21  */
22
23 /**
24  * SECTION:gstqueuearray
25  * @title: GstQueueArray
26  * @short_description: Array based queue object
27  *
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.
31  */
32
33
34 #include <string.h>
35 #include <gst/gst.h>
36 #include "gstqueuearray.h"
37
38 struct _GstQueueArray
39 {
40   /* < private > */
41   guint8 *array;
42   guint size;
43   guint head;
44   guint tail;
45   guint length;
46   guint elt_size;
47   gboolean struct_array;
48 };
49
50 /**
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
54  *
55  * Allocates a new #GstQueueArray object for elements (e.g. structures)
56  * of size @struct_size, with an initial queue size of @initial_size.
57  *
58  * Returns: a new #GstQueueArray object
59  *
60  * Since: 1.6
61  */
62 GstQueueArray *
63 gst_queue_array_new_for_struct (gsize struct_size, guint initial_size)
64 {
65   GstQueueArray *array;
66
67   g_return_val_if_fail (struct_size > 0, NULL);
68
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);
73   array->head = 0;
74   array->tail = 0;
75   array->length = 0;
76   array->struct_array = TRUE;
77   return array;
78 }
79
80 /**
81  * gst_queue_array_new: (skip)
82  * @initial_size: Initial size of the new queue
83  *
84  * Allocates a new #GstQueueArray object with an initial
85  * queue size of @initial_size.
86  *
87  * Returns: a new #GstQueueArray object
88  *
89  * Since: 1.2
90  */
91 GstQueueArray *
92 gst_queue_array_new (guint initial_size)
93 {
94   GstQueueArray *array;
95
96   array = gst_queue_array_new_for_struct (sizeof (gpointer), initial_size);
97   array->struct_array = FALSE;
98   return array;
99 }
100
101 /**
102  * gst_queue_array_free: (skip)
103  * @array: a #GstQueueArray object
104  *
105  * Frees queue @array and all memory associated to it.
106  *
107  * Since: 1.2
108  */
109 void
110 gst_queue_array_free (GstQueueArray * array)
111 {
112   g_free (array->array);
113   g_slice_free (GstQueueArray, array);
114 }
115
116 /**
117  * gst_queue_array_pop_head_struct: (skip)
118  * @array: a #GstQueueArray object
119  *
120  * Returns the head of the queue @array and removes it from the queue.
121  *
122  * Returns: pointer to element or struct, or NULL if @array was empty. The
123  *    data pointed to by the returned pointer stays valid only as long as
124  *    the queue array is not modified further!
125  *
126  * Since: 1.6
127  */
128 gpointer
129 gst_queue_array_pop_head_struct (GstQueueArray * array)
130 {
131   gpointer p_struct;
132
133   /* empty array */
134   if (G_UNLIKELY (array->length == 0))
135     return NULL;
136
137   p_struct = array->array + (array->elt_size * array->head);
138
139   array->head++;
140   array->head %= array->size;
141   array->length--;
142
143   return p_struct;
144 }
145
146 /**
147  * gst_queue_array_pop_head: (skip)
148  * @array: a #GstQueueArray object
149  *
150  * Returns and head of the queue @array and removes
151  * it from the queue.
152  *
153  * Returns: The head of the queue
154  *
155  * Since: 1.2
156  */
157 gpointer
158 gst_queue_array_pop_head (GstQueueArray * array)
159 {
160   gpointer ret;
161
162   /* empty array */
163   if (G_UNLIKELY (array->length == 0))
164     return NULL;
165
166   ret = *(gpointer *) (array->array + (sizeof (gpointer) * array->head));
167   array->head++;
168   array->head %= array->size;
169   array->length--;
170   return ret;
171 }
172
173 /**
174  * gst_queue_array_peek_head_struct: (skip)
175  * @array: a #GstQueueArray object
176  *
177  * Returns the head of the queue @array without removing it from the queue.
178  *
179  * Returns: pointer to element or struct, or NULL if @array was empty. The
180  *    data pointed to by the returned pointer stays valid only as long as
181  *    the queue array is not modified further!
182  *
183  * Since: 1.6
184  */
185 gpointer
186 gst_queue_array_peek_head_struct (GstQueueArray * array)
187 {
188   /* empty array */
189   if (G_UNLIKELY (array->length == 0))
190     return NULL;
191
192   return array->array + (array->elt_size * array->head);
193 }
194
195 /**
196  * gst_queue_array_peek_head: (skip)
197  * @array: a #GstQueueArray object
198  *
199  * Returns the head of the queue @array and does not
200  * remove it from the queue.
201  *
202  * Returns: The head of the queue
203  *
204  * Since: 1.2
205  */
206 gpointer
207 gst_queue_array_peek_head (GstQueueArray * array)
208 {
209   /* empty array */
210   if (G_UNLIKELY (array->length == 0))
211     return NULL;
212
213   return *(gpointer *) (array->array + (sizeof (gpointer) * array->head));
214 }
215
216 static void
217 gst_queue_array_do_expand (GstQueueArray * array)
218 {
219   guint elt_size = array->elt_size;
220   /* newsize is 50% bigger */
221   guint oldsize = array->size;
222   guint newsize = MAX ((3 * oldsize) / 2, oldsize + 1);
223
224   /* copy over data */
225   if (array->tail != 0) {
226     guint8 *array2 = g_malloc0 (elt_size * newsize);
227     guint t1 = array->head;
228     guint t2 = oldsize - array->head;
229
230     /* [0-----TAIL][HEAD------SIZE]
231      *
232      * We want to end up with
233      * [HEAD------------------TAIL][----FREEDATA------NEWSIZE]
234      *
235      * 1) move [HEAD-----SIZE] part to beginning of new array
236      * 2) move [0-------TAIL] part new array, after previous part
237      */
238
239     memcpy (array2, array->array + (elt_size * array->head), t2 * elt_size);
240     memcpy (array2 + t2 * elt_size, array->array, t1 * elt_size);
241
242     g_free (array->array);
243     array->array = array2;
244     array->head = 0;
245   } else {
246     /* Fast path, we just need to grow the array */
247     array->array = g_realloc (array->array, elt_size * newsize);
248     memset (array->array + elt_size * oldsize, 0,
249         elt_size * (newsize - oldsize));
250   }
251   array->tail = oldsize;
252   array->size = newsize;
253 }
254
255 /**
256  * gst_queue_array_push_element_tail: (skip)
257  * @array: a #GstQueueArray object
258  * @p_struct: address of element or structure to push to the tail of the queue
259  *
260  * Pushes the element at address @p_struct to the tail of the queue @array
261  * (Copies the contents of a structure of the struct_size specified when
262  * creating the queue into the array).
263  *
264  * Since: 1.6
265  */
266 void
267 gst_queue_array_push_tail_struct (GstQueueArray * array, gpointer p_struct)
268 {
269   guint elt_size;
270
271   g_return_if_fail (p_struct != NULL);
272
273   elt_size = array->elt_size;
274
275   /* Check if we need to make room */
276   if (G_UNLIKELY (array->length == array->size))
277     gst_queue_array_do_expand (array);
278
279   memcpy (array->array + elt_size * array->tail, p_struct, elt_size);
280   array->tail++;
281   array->tail %= array->size;
282   array->length++;
283 }
284
285 /**
286  * gst_queue_array_push_tail: (skip)
287  * @array: a #GstQueueArray object
288  * @data: object to push
289  *
290  * Pushes @data to the tail of the queue @array.
291  *
292  * Since: 1.2
293  */
294 void
295 gst_queue_array_push_tail (GstQueueArray * array, gpointer data)
296 {
297   /* Check if we need to make room */
298   if (G_UNLIKELY (array->length == array->size))
299     gst_queue_array_do_expand (array);
300
301   *(gpointer *) (array->array + sizeof (gpointer) * array->tail) = data;
302   array->tail++;
303   array->tail %= array->size;
304   array->length++;
305 }
306
307 /**
308  * gst_queue_array_is_empty: (skip)
309  * @array: a #GstQueueArray object
310  *
311  * Checks if the queue @array is empty.
312  *
313  * Returns: %TRUE if the queue @array is empty
314  *
315  * Since: 1.2
316  */
317 gboolean
318 gst_queue_array_is_empty (GstQueueArray * array)
319 {
320   return (array->length == 0);
321 }
322
323
324 /**
325  * gst_queue_array_drop_struct: (skip)
326  * @array: a #GstQueueArray object
327  * @idx: index to drop
328  * @p_struct: address into which to store the data of the dropped structure, or NULL
329  *
330  * Drops the queue element at position @idx from queue @array and copies the
331  * data of the element or structure that was removed into @p_struct if
332  * @p_struct is set (not NULL).
333  *
334  * Returns: TRUE on success, or FALSE on error
335  *
336  * Since: 1.6
337  */
338 gboolean
339 gst_queue_array_drop_struct (GstQueueArray * array, guint idx,
340     gpointer p_struct)
341 {
342   int first_item_index, last_item_index;
343   guint elt_size;
344
345   g_return_val_if_fail (array->length > 0, FALSE);
346   g_return_val_if_fail (idx < array->size, FALSE);
347
348   elt_size = array->elt_size;
349
350   first_item_index = array->head;
351
352   /* tail points to the first free spot */
353   last_item_index = (array->tail - 1 + array->size) % array->size;
354
355   if (p_struct != NULL)
356     memcpy (p_struct, array->array + elt_size * idx, elt_size);
357
358   /* simple case idx == first item */
359   if (idx == first_item_index) {
360     /* move the head plus one */
361     array->head++;
362     array->head %= array->size;
363     array->length--;
364     return TRUE;
365   }
366
367   /* simple case idx == last item */
368   if (idx == last_item_index) {
369     /* move tail minus one, potentially wrapping */
370     array->tail = (array->tail - 1 + array->size) % array->size;
371     array->length--;
372     return TRUE;
373   }
374
375   /* non-wrapped case */
376   if (first_item_index < last_item_index) {
377     g_assert (first_item_index < idx && idx < last_item_index);
378     /* move everything beyond idx one step towards zero in array */
379     memmove (array->array + elt_size * idx,
380         array->array + elt_size * (idx + 1),
381         (last_item_index - idx) * elt_size);
382     /* tail might wrap, ie if tail == 0 (and last_item_index == size) */
383     array->tail = (array->tail - 1 + array->size) % array->size;
384     array->length--;
385     return TRUE;
386   }
387
388   /* only wrapped cases left */
389   g_assert (first_item_index > last_item_index);
390
391   if (idx < last_item_index) {
392     /* idx is before last_item_index, move data towards zero */
393     memmove (array->array + elt_size * idx,
394         array->array + elt_size * (idx + 1),
395         (last_item_index - idx) * elt_size);
396     /* tail should not wrap in this case! */
397     g_assert (array->tail > 0);
398     array->tail--;
399     array->length--;
400     return TRUE;
401   }
402
403   if (idx > first_item_index) {
404     /* idx is after first_item_index, move data to higher indices */
405     memmove (array->array + elt_size * (first_item_index + 1),
406         array->array + elt_size * first_item_index,
407         (idx - first_item_index) * elt_size);
408     array->head++;
409     /* head should not wrap in this case! */
410     g_assert (array->head < array->size);
411     array->length--;
412     return TRUE;
413   }
414
415   g_return_val_if_reached (FALSE);
416 }
417
418 /**
419  * gst_queue_array_drop_element: (skip)
420  * @array: a #GstQueueArray object
421  * @idx: index to drop
422  *
423  * Drops the queue element at position @idx from queue @array.
424  *
425  * Returns: the dropped element
426  *
427  * Since: 1.2
428  */
429 gpointer
430 gst_queue_array_drop_element (GstQueueArray * array, guint idx)
431 {
432   gpointer ptr;
433
434   if (!gst_queue_array_drop_struct (array, idx, &ptr))
435     return NULL;
436
437   return ptr;
438 }
439
440 /**
441  * gst_queue_array_find: (skip)
442  * @array: a #GstQueueArray object
443  * @func: (allow-none): comparison function, or %NULL to find @data by value
444  * @data: data for comparison function
445  *
446  * Finds an element in the queue @array, either by comparing every element
447  * with @func or by looking up @data if no compare function @func is provided,
448  * and returning the index of the found element.
449  *
450  * Note that the index is not 0-based, but an internal index number with a
451  * random offset. The index can be used in connection with
452  * gst_queue_array_drop_element(). FIXME: return index 0-based and make
453  * gst_queue_array_drop_element() take a 0-based index.
454  *
455  * Returns: Index of the found element or -1 if nothing was found.
456  *
457  * Since: 1.2
458  */
459 guint
460 gst_queue_array_find (GstQueueArray * array, GCompareFunc func, gpointer data)
461 {
462   gpointer p_element;
463   guint elt_size;
464   guint i;
465
466   /* For struct arrays we need to implement this differently so that
467    * the user gets a pointer to the element data not the dereferenced
468    * pointer itself */
469   g_return_val_if_fail (array->struct_array == FALSE, -1);
470
471   elt_size = array->elt_size;
472
473   if (func != NULL) {
474     /* Scan from head to tail */
475     for (i = 0; i < array->length; i++) {
476       p_element = array->array + ((i + array->head) % array->size) * elt_size;
477       if (func (*(gpointer *) p_element, data) == 0)
478         return (i + array->head) % array->size;
479     }
480   } else {
481     for (i = 0; i < array->length; i++) {
482       p_element = array->array + ((i + array->head) % array->size) * elt_size;
483       if (*(gpointer *) p_element == data)
484         return (i + array->head) % array->size;
485     }
486   }
487
488   return -1;
489 }
490
491 /**
492  * gst_queue_array_get_length: (skip)
493  * @array: a #GstQueueArray object
494  *
495  * Returns the length of the queue @array
496  *
497  * Returns: the length of the queue @array.
498  *
499  * Since: 1.2
500  */
501 guint
502 gst_queue_array_get_length (GstQueueArray * array)
503 {
504   return array->length;
505 }