/* GStreamer
* Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
*
* gstqueuearray.c:
*
/**
* 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 managment by a large factor.
+ * caused by memory management by a large factor.
*/
struct _GstQueueArray
{
/* < private > */
- gpointer *array;
+ guint8 *array;
guint size;
guint head;
guint tail;
guint length;
+ guint elt_size;
+ gboolean struct_array;
};
/**
- * gst_queue_array_new:
+ * 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;
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:
+ * gst_queue_array_free: (skip)
* @array: a #GstQueueArray object
*
* Frees queue @array and all memory associated to it.
}
/**
- * gst_queue_array_pop_head:
+ * 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;
+
+ /* 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
*
* Returns and head of the queue @array and removes
/* 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--;
}
/**
- * gst_queue_array_pop_head:
+ * 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)
+{
+ /* 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
/* empty array */
if (G_UNLIKELY (array->length == 0))
return NULL;
- return array->array[array->head];
+
+ return *(gpointer *) (array->array + (sizeof (gpointer) * array->head));
+}
+
+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);
+
+ 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++;
}
/**
- * gst_queue_array_push_tail:
+ * gst_queue_array_push_tail: (skip)
* @array: a #GstQueueArray object
* @data: object to push
*
gst_queue_array_push_tail (GstQueueArray * array, gpointer data)
{
/* Check if we need to make room */
- if (G_UNLIKELY (array->length == array->size)) {
- /* newsize is 50% bigger */
- guint newsize = (3 * array->size) / 2;
-
- /* 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_is_empty:
+ * gst_queue_array_is_empty: (skip)
* @array: a #GstQueueArray object
*
* Checks if the queue @array is empty.
return (array->length == 0);
}
+
/**
- * gst_queue_array_drop_element:
+ * 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 elt_size;
+
+ g_return_val_if_fail (array->length > 0, FALSE);
+ g_return_val_if_fail (idx < array->size, FALSE);
- g_return_val_if_fail (array->length > 0, NULL);
- g_return_val_if_fail (idx < array->size, NULL);
+ 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 * idx, elt_size);
/* simple case idx == first item */
if (idx == first_item_index) {
array->head++;
array->head %= array->size;
array->length--;
- return element;
+ return TRUE;
}
/* simple case idx == last item */
/* 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));
+ memmove (array->array + elt_size * idx,
+ array->array + elt_size * (idx + 1),
+ (last_item_index - 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 */
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));
+ memmove (array->array + elt_size * idx,
+ array->array + elt_size * (idx + 1),
+ (last_item_index - 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) {
- element = array->array[idx];
/* 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));
+ memmove (array->array + elt_size * (first_item_index + 1),
+ array->array + elt_size * first_item_index,
+ (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_find:
+ * 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;
+}
+
+/**
+ * gst_queue_array_find: (skip)
* @array: a #GstQueueArray object
* @func: (allow-none): comparison function, or %NULL to find @data by value
* @data: data for comparison function
* 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
- * _drop_element() take a 0-based index.
+ * gst_queue_array_drop_element() take a 0-based index.
*
* Returns: Index of the found element or -1 if nothing was found.
*
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->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)
+ p_element = array->array + ((i + array->head) % array->size) * elt_size;
+ if (func (*(gpointer *) p_element, data) == 0)
return (i + array->head) % array->size;
}
} else {
for (i = 0; i < array->length; i++) {
- if (array->array[(i + array->head) % array->size] == data)
+ p_element = array->array + ((i + array->head) % array->size) * elt_size;
+ if (*(gpointer *) p_element == data)
return (i + array->head) % array->size;
}
}
}
/**
- * gst_queue_array_get_length:
+ * gst_queue_array_get_length: (skip)
* @array: a #GstQueueArray object
*
* Returns the length of the queue @array