X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=libs%2Fgst%2Fbase%2Fgstqueuearray.c;h=1cf17475182702d7174f176357c90d9c2d95b946;hb=5bf13cdd5314bc3c6c81bd620e712acdcab14eb2;hp=5a69b698d5404b1d43be417fe2d001362179fede;hpb=828ceacc68551bbf41223b050bc89d4c75cddf25;p=platform%2Fupstream%2Fgstreamer.git diff --git a/libs/gst/base/gstqueuearray.c b/libs/gst/base/gstqueuearray.c index 5a69b69..1cf1747 100644 --- a/libs/gst/base/gstqueuearray.c +++ b/libs/gst/base/gstqueuearray.c @@ -1,5 +1,6 @@ /* GStreamer * Copyright (C) 2009 Edward Hervey + * Copyright (C) 2015 Tim-Philipp Müller * * gstqueuearray.c: * @@ -21,13 +22,16 @@ /** * SECTION:gstqueuearray + * @title: GstQueueArray * @short_description: Array based queue object * * #GstQueueArray is an object that provides standard queue functionality * based on an array instead of linked lists. This reduces the overhead * caused by memory management by a large factor. */ - +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif #include #include @@ -36,38 +40,67 @@ struct _GstQueueArray { /* < private > */ - gpointer *array; + guint8 *array; guint size; guint head; guint tail; guint length; + guint elt_size; + gboolean struct_array; + GDestroyNotify clear_func; }; /** - * gst_queue_array_new: (skip) + * gst_queue_array_new_for_struct: (skip) + * @struct_size: Size of each element (e.g. structure) in the array * @initial_size: Initial size of the new queue * - * Allocates a new #GstQueueArray object with an initial - * queue size of @initial_size. + * Allocates a new #GstQueueArray object for elements (e.g. structures) + * of size @struct_size, with an initial queue size of @initial_size. * * Returns: a new #GstQueueArray object * - * Since: 1.2 + * Since: 1.6 */ GstQueueArray * -gst_queue_array_new (guint initial_size) +gst_queue_array_new_for_struct (gsize struct_size, guint initial_size) { GstQueueArray *array; + g_return_val_if_fail (struct_size > 0, NULL); + array = g_slice_new (GstQueueArray); + array->elt_size = struct_size; array->size = initial_size; - array->array = g_new0 (gpointer, initial_size); + array->array = g_malloc0 (struct_size * initial_size); array->head = 0; array->tail = 0; array->length = 0; + array->struct_array = TRUE; + array->clear_func = NULL; return array; } +/** + * gst_queue_array_new: (skip) + * @initial_size: Initial size of the new queue + * + * Allocates a new #GstQueueArray object with an initial + * queue size of @initial_size. + * + * Returns: a new #GstQueueArray object + * + * Since: 1.2 + */ +GstQueueArray * +gst_queue_array_new (guint initial_size) +{ + GstQueueArray *array; + + array = gst_queue_array_new_for_struct (sizeof (gpointer), initial_size); + array->struct_array = FALSE; + return array; +} /** * gst_queue_array_free: (skip) @@ -80,11 +113,110 @@ gst_queue_array_new (guint initial_size) void gst_queue_array_free (GstQueueArray * array) { + g_return_if_fail (array != NULL); + gst_queue_array_clear (array); g_free (array->array); g_slice_free (GstQueueArray, array); } /** + * gst_queue_array_set_clear_func: (skip) + * @array: a #GstQueueArray object + * @clear_func: a function to clear an element of @array + * + * Sets a function to clear an element of @array. + * + * The @clear_func will be called when an element in the array + * data segment is removed and when the array is freed and data + * segment is deallocated as well. @clear_func will be passed a + * pointer to the element to clear, rather than the element itself. + * + * Note that in contrast with other uses of #GDestroyNotify + * functions, @clear_func is expected to clear the contents of + * the array element it is given, but not free the element itself. + * + * Since: 1.16 + */ +void +gst_queue_array_set_clear_func (GstQueueArray * array, + GDestroyNotify clear_func) +{ + g_return_if_fail (array != NULL); + array->clear_func = clear_func; +} + +static void +gst_queue_array_clear_idx (GstQueueArray * array, guint idx) +{ + guint pos; + + if (!array->clear_func) + return; + + pos = (idx + array->head) % array->size; + if (array->struct_array) + array->clear_func (array->array + pos * array->elt_size); + else + array->clear_func (*(gpointer *) (array->array + pos * array->elt_size)); +} + +/** + * gst_queue_array_clear: (skip) + * @array: a #GstQueueArray object + * + * Clears queue @array and frees all memory associated to it. + * + * Since: 1.16 + */ +void +gst_queue_array_clear (GstQueueArray * array) +{ + g_return_if_fail (array != NULL); + + if (array->clear_func != NULL) { + guint i; + + for (i = 0; i < array->length; i++) { + gst_queue_array_clear_idx (array, i); + } + } + + array->head = 0; + array->tail = 0; + array->length = 0; +} + +/** + * gst_queue_array_pop_head_struct: (skip) + * @array: a #GstQueueArray object + * + * Returns the head of the queue @array and removes it from the queue. + * + * Returns: pointer to element or struct, or NULL if @array was empty. The + * data pointed to by the returned pointer stays valid only as long as + * the queue array is not modified further! + * + * Since: 1.6 + */ +gpointer +gst_queue_array_pop_head_struct (GstQueueArray * array) +{ + gpointer p_struct; + g_return_val_if_fail (array != NULL, NULL); + /* empty array */ + if (G_UNLIKELY (array->length == 0)) + return NULL; + + p_struct = array->array + (array->elt_size * array->head); + + array->head++; + array->head %= array->size; + array->length--; + + return p_struct; +} + +/** * gst_queue_array_pop_head: (skip) * @array: a #GstQueueArray object * @@ -99,11 +231,13 @@ gpointer gst_queue_array_pop_head (GstQueueArray * array) { gpointer ret; + g_return_val_if_fail (array != NULL, NULL); /* empty array */ if (G_UNLIKELY (array->length == 0)) return NULL; - ret = array->array[array->head]; + + ret = *(gpointer *) (array->array + (sizeof (gpointer) * array->head)); array->head++; array->head %= array->size; array->length--; @@ -111,10 +245,33 @@ gst_queue_array_pop_head (GstQueueArray * array) } /** + * gst_queue_array_peek_head_struct: (skip) + * @array: a #GstQueueArray object + * + * Returns the head of the queue @array without removing it from the queue. + * + * Returns: pointer to element or struct, or NULL if @array was empty. The + * data pointed to by the returned pointer stays valid only as long as + * the queue array is not modified further! + * + * Since: 1.6 + */ +gpointer +gst_queue_array_peek_head_struct (GstQueueArray * array) +{ + g_return_val_if_fail (array != NULL, NULL); + /* empty array */ + if (G_UNLIKELY (array->length == 0)) + return NULL; + + return array->array + (array->elt_size * array->head); +} + +/** * gst_queue_array_peek_head: (skip) * @array: a #GstQueueArray object * - * Returns and head of the queue @array and does not + * Returns the head of the queue @array and does not * remove it from the queue. * * Returns: The head of the queue @@ -124,10 +281,121 @@ gst_queue_array_pop_head (GstQueueArray * array) gpointer gst_queue_array_peek_head (GstQueueArray * array) { + g_return_val_if_fail (array != NULL, NULL); /* empty array */ if (G_UNLIKELY (array->length == 0)) return NULL; - return array->array[array->head]; + + return *(gpointer *) (array->array + (sizeof (gpointer) * array->head)); +} + +/** + * gst_queue_array_peek_nth: (skip) + * + * Returns the item at @idx in @array, but does not remove it from the queue. + * + * Returns: The item, or %NULL if @idx was out of bounds + * + * Since: 1.16 + */ +gpointer +gst_queue_array_peek_nth (GstQueueArray * array, guint idx) +{ + g_return_val_if_fail (array != NULL, NULL); + g_return_val_if_fail (idx < array->length, NULL); + + idx = (array->head + idx) % array->size; + + return *(gpointer *) (array->array + (sizeof (gpointer) * idx)); +} + +/** + * gst_queue_array_peek_nth_struct: (skip) + * + * Returns the item at @idx in @array, but does not remove it from the queue. + * + * Returns: The item, or %NULL if @idx was out of bounds + * + * Since: 1.16 + */ +gpointer +gst_queue_array_peek_nth_struct (GstQueueArray * array, guint idx) +{ + g_return_val_if_fail (array != NULL, NULL); + g_return_val_if_fail (idx < array->length, NULL); + + idx = (array->head + idx) % array->size; + + return array->array + (array->elt_size * idx); +} + +static void +gst_queue_array_do_expand (GstQueueArray * array) +{ + guint elt_size = array->elt_size; + /* newsize is 50% bigger */ + guint oldsize = array->size; + guint newsize = MAX ((3 * oldsize) / 2, oldsize + 1); + + /* copy over data */ + if (array->tail != 0) { + guint8 *array2 = g_malloc0 (elt_size * newsize); + guint t1 = array->head; + guint t2 = oldsize - array->head; + + /* [0-----TAIL][HEAD------SIZE] + * + * We want to end up with + * [HEAD------------------TAIL][----FREEDATA------NEWSIZE] + * + * 1) move [HEAD-----SIZE] part to beginning of new array + * 2) move [0-------TAIL] part new array, after previous part + */ + + memcpy (array2, array->array + (elt_size * array->head), t2 * elt_size); + memcpy (array2 + t2 * elt_size, array->array, t1 * elt_size); + + g_free (array->array); + array->array = array2; + array->head = 0; + } else { + /* Fast path, we just need to grow the array */ + array->array = g_realloc (array->array, elt_size * newsize); + memset (array->array + elt_size * oldsize, 0, + elt_size * (newsize - oldsize)); + } + array->tail = oldsize; + array->size = newsize; +} + +/** + * gst_queue_array_push_element_tail: (skip) + * @array: a #GstQueueArray object + * @p_struct: address of element or structure to push to the tail of the queue + * + * Pushes the element at address @p_struct to the tail of the queue @array + * (Copies the contents of a structure of the struct_size specified when + * creating the queue into the array). + * + * Since: 1.6 + */ +void +gst_queue_array_push_tail_struct (GstQueueArray * array, gpointer p_struct) +{ + guint elt_size; + + g_return_if_fail (p_struct != NULL); + g_return_if_fail (array != NULL); + elt_size = array->elt_size; + + /* Check if we need to make room */ + if (G_UNLIKELY (array->length == array->size)) + gst_queue_array_do_expand (array); + + memcpy (array->array + elt_size * array->tail, p_struct, elt_size); + array->tail++; + array->tail %= array->size; + array->length++; } /** @@ -142,47 +410,145 @@ gst_queue_array_peek_head (GstQueueArray * array) void gst_queue_array_push_tail (GstQueueArray * array, gpointer data) { + g_return_if_fail (array != NULL); + /* Check if we need to make room */ - if (G_UNLIKELY (array->length == array->size)) { - /* newsize is 50% bigger */ - guint newsize = MAX ((3 * array->size) / 2, array->size + 1); - - /* copy over data */ - if (array->tail != 0) { - gpointer *array2 = g_new0 (gpointer, newsize); - guint t1 = array->head; - guint t2 = array->size - array->head; - - /* [0-----TAIL][HEAD------SIZE] - * - * We want to end up with - * [HEAD------------------TAIL][----FREEDATA------NEWSIZE] - * - * 1) move [HEAD-----SIZE] part to beginning of new array - * 2) move [0-------TAIL] part new array, after previous part - */ - - memcpy (array2, &array->array[array->head], t2 * sizeof (gpointer)); - memcpy (&array2[t2], array->array, t1 * sizeof (gpointer)); - - g_free (array->array); - array->array = array2; - array->head = 0; - } else { - /* Fast path, we just need to grow the array */ - array->array = g_renew (gpointer, array->array, newsize); - } - array->tail = array->size; - array->size = newsize; - } + if (G_UNLIKELY (array->length == array->size)) + gst_queue_array_do_expand (array); - array->array[array->tail] = data; + *(gpointer *) (array->array + sizeof (gpointer) * array->tail) = data; array->tail++; array->tail %= array->size; array->length++; } /** + * gst_queue_array_peek_tail: (skip) + * @array: a #GstQueueArray object + * + * Returns the tail of the queue @array, but does not remove it from the queue. + * + * Returns: The tail of the queue + * + * Since: 1.14 + */ +gpointer +gst_queue_array_peek_tail (GstQueueArray * array) +{ + guint len, idx; + + g_return_val_if_fail (array != NULL, NULL); + + len = array->length; + + /* empty array */ + if (len == 0) + return NULL; + + idx = (array->head + (len - 1)) % array->size; + + return *(gpointer *) (array->array + (sizeof (gpointer) * idx)); +} + +/** + * gst_queue_array_peek_tail_struct: (skip) + * @array: a #GstQueueArray object + * + * Returns the tail of the queue @array, but does not remove it from the queue. + * + * Returns: The tail of the queue + * + * Since: 1.14 + */ +gpointer +gst_queue_array_peek_tail_struct (GstQueueArray * array) +{ + guint len, idx; + + g_return_val_if_fail (array != NULL, NULL); + + len = array->length; + + /* empty array */ + if (len == 0) + return NULL; + + idx = (array->head + (len - 1)) % array->size; + + return array->array + (array->elt_size * idx); +} + +/** + * gst_queue_array_pop_tail: (skip) + * @array: a #GstQueueArray object + * + * Returns the tail of the queue @array and removes + * it from the queue. + * + * Returns: The tail of the queue + * + * Since: 1.14 + */ +gpointer +gst_queue_array_pop_tail (GstQueueArray * array) +{ + gpointer ret; + guint len, idx; + + g_return_val_if_fail (array != NULL, NULL); + + len = array->length; + + /* empty array */ + if (len == 0) + return NULL; + + idx = (array->head + (len - 1)) % array->size; + + ret = *(gpointer *) (array->array + (sizeof (gpointer) * idx)); + + array->tail = idx; + array->length--; + + return ret; +} + +/** + * gst_queue_array_pop_tail_struct: (skip) + * @array: a #GstQueueArray object + * + * Returns the tail of the queue @array and removes + * it from the queue. + * + * Returns: The tail of the queue + * + * Since: 1.14 + */ +gpointer +gst_queue_array_pop_tail_struct (GstQueueArray * array) +{ + gpointer ret; + guint len, idx; + + g_return_val_if_fail (array != NULL, NULL); + + len = array->length; + + /* empty array */ + if (len == 0) + return NULL; + + idx = (array->head + (len - 1)) % array->size; + + ret = array->array + (array->elt_size * idx); + + array->tail = idx; + array->length--; + + return ret; +} + +/** * gst_queue_array_is_empty: (skip) * @array: a #GstQueueArray object * @@ -195,92 +561,149 @@ gst_queue_array_push_tail (GstQueueArray * array, gpointer data) gboolean gst_queue_array_is_empty (GstQueueArray * array) { + g_return_val_if_fail (array != NULL, FALSE); return (array->length == 0); } + /** - * gst_queue_array_drop_element: (skip) + * gst_queue_array_drop_struct: (skip) * @array: a #GstQueueArray object * @idx: index to drop + * @p_struct: address into which to store the data of the dropped structure, or NULL * - * Drops the queue element at position @idx from queue @array. + * Drops the queue element at position @idx from queue @array and copies the + * data of the element or structure that was removed into @p_struct if + * @p_struct is set (not NULL). * - * Returns: the dropped element + * Returns: TRUE on success, or FALSE on error * - * Since: 1.2 + * Since: 1.6 */ -gpointer -gst_queue_array_drop_element (GstQueueArray * array, guint idx) +gboolean +gst_queue_array_drop_struct (GstQueueArray * array, guint idx, + gpointer p_struct) { int first_item_index, last_item_index; - gpointer element; + guint actual_idx; + guint elt_size; + + g_return_val_if_fail (array != NULL, FALSE); + actual_idx = (array->head + idx) % array->size; - g_return_val_if_fail (array->length > 0, NULL); - g_return_val_if_fail (idx < array->size, NULL); + g_return_val_if_fail (array->length > 0, FALSE); + g_return_val_if_fail (actual_idx < array->size, FALSE); + + elt_size = array->elt_size; first_item_index = array->head; /* tail points to the first free spot */ last_item_index = (array->tail - 1 + array->size) % array->size; - element = array->array[idx]; + if (p_struct != NULL) + memcpy (p_struct, array->array + elt_size * actual_idx, elt_size); + + /* simple case actual_idx == first item */ + if (actual_idx == first_item_index) { + /* clear current head position if needed */ + if (p_struct == NULL) + gst_queue_array_clear_idx (array, idx); - /* simple case idx == first item */ - if (idx == first_item_index) { /* move the head plus one */ array->head++; array->head %= array->size; array->length--; - return element; + return TRUE; } /* simple case idx == last item */ - if (idx == last_item_index) { + if (actual_idx == last_item_index) { + /* clear current tail position if needed */ + if (p_struct == NULL) + gst_queue_array_clear_idx (array, idx); + /* move tail minus one, potentially wrapping */ array->tail = (array->tail - 1 + array->size) % array->size; array->length--; - return element; + return TRUE; } /* non-wrapped case */ if (first_item_index < last_item_index) { - g_assert (first_item_index < idx && idx < last_item_index); - /* move everything beyond idx one step towards zero in array */ - memmove (&array->array[idx], - &array->array[idx + 1], (last_item_index - idx) * sizeof (gpointer)); + /* clear idx if needed */ + if (p_struct == NULL) + gst_queue_array_clear_idx (array, idx); + + g_assert (first_item_index < actual_idx && actual_idx < last_item_index); + /* move everything beyond actual_idx one step towards zero in array */ + memmove (array->array + elt_size * actual_idx, + array->array + elt_size * (actual_idx + 1), + (last_item_index - actual_idx) * elt_size); /* tail might wrap, ie if tail == 0 (and last_item_index == size) */ array->tail = (array->tail - 1 + array->size) % array->size; array->length--; - return element; + return TRUE; } /* only wrapped cases left */ g_assert (first_item_index > last_item_index); - if (idx < last_item_index) { - /* idx is before last_item_index, move data towards zero */ - memmove (&array->array[idx], - &array->array[idx + 1], (last_item_index - idx) * sizeof (gpointer)); + if (actual_idx < last_item_index) { + /* clear idx if needed */ + if (p_struct == NULL) + gst_queue_array_clear_idx (array, idx); + + /* actual_idx is before last_item_index, move data towards zero */ + memmove (array->array + elt_size * actual_idx, + array->array + elt_size * (actual_idx + 1), + (last_item_index - actual_idx) * elt_size); /* tail should not wrap in this case! */ g_assert (array->tail > 0); array->tail--; array->length--; - return element; + return TRUE; } - if (idx > first_item_index) { - /* idx is after first_item_index, move data to higher indices */ - memmove (&array->array[first_item_index + 1], - &array->array[first_item_index], - (idx - first_item_index) * sizeof (gpointer)); + if (actual_idx > first_item_index) { + /* clear idx if needed */ + if (p_struct == NULL) + gst_queue_array_clear_idx (array, idx); + + /* actual_idx is after first_item_index, move data to higher indices */ + memmove (array->array + elt_size * (first_item_index + 1), + array->array + elt_size * first_item_index, + (actual_idx - first_item_index) * elt_size); array->head++; /* head should not wrap in this case! */ g_assert (array->head < array->size); array->length--; - return element; + return TRUE; } - g_return_val_if_reached (NULL); + g_return_val_if_reached (FALSE); +} + +/** + * gst_queue_array_drop_element: (skip) + * @array: a #GstQueueArray object + * @idx: index to drop + * + * Drops the queue element at position @idx from queue @array. + * + * Returns: the dropped element + * + * Since: 1.2 + */ +gpointer +gst_queue_array_drop_element (GstQueueArray * array, guint idx) +{ + gpointer ptr; + + if (!gst_queue_array_drop_struct (array, idx, &ptr)) + return NULL; + + return ptr; } /** @@ -293,11 +716,6 @@ gst_queue_array_drop_element (GstQueueArray * array, guint idx) * with @func or by looking up @data if no compare function @func is provided, * and returning the index of the found element. * - * Note that the index is not 0-based, but an internal index number with a - * random offset. The index can be used in connection with - * gst_queue_array_drop_element(). FIXME: return index 0-based and make - * gst_queue_array_drop_element() take a 0-based index. - * * Returns: Index of the found element or -1 if nothing was found. * * Since: 1.2 @@ -305,18 +723,31 @@ gst_queue_array_drop_element (GstQueueArray * array, guint idx) guint gst_queue_array_find (GstQueueArray * array, GCompareFunc func, gpointer data) { + gpointer p_element; + guint elt_size; guint i; + /* For struct arrays we need to implement this differently so that + * the user gets a pointer to the element data not the dereferenced + * pointer itself */ + + g_return_val_if_fail (array != NULL, -1); + g_return_val_if_fail (array->struct_array == FALSE, -1); + + elt_size = array->elt_size; + if (func != NULL) { /* Scan from head to tail */ for (i = 0; i < array->length; i++) { - if (func (array->array[(i + array->head) % array->size], data) == 0) - return (i + array->head) % array->size; + p_element = array->array + ((i + array->head) % array->size) * elt_size; + if (func (*(gpointer *) p_element, data) == 0) + return i; } } else { for (i = 0; i < array->length; i++) { - if (array->array[(i + array->head) % array->size] == data) - return (i + array->head) % array->size; + p_element = array->array + ((i + array->head) % array->size) * elt_size; + if (*(gpointer *) p_element == data) + return i; } } @@ -336,5 +767,6 @@ gst_queue_array_find (GstQueueArray * array, GCompareFunc func, gpointer data) guint gst_queue_array_get_length (GstQueueArray * array) { + g_return_val_if_fail (array != NULL, 0); return array->length; }