queuearray: allow storing of structs in addition to pointers
authorTim-Philipp Müller <tim@centricular.com>
Sat, 30 May 2015 12:07:50 +0000 (13:07 +0100)
committerTim-Philipp Müller <tim@centricular.com>
Sat, 4 Jul 2015 10:03:51 +0000 (11:03 +0100)
This way we don't have to allocate/free temporary structs
for storing things in the queue array.

API: gst_queue_array_new_for_struct()
API: gst_queue_array_push_tail_struct()
API: gst_queue_array_peek_head_struct()
API: gst_queue_array_pop_head_struct()
API: gst_queue_array_drop_struct()

https://bugzilla.gnome.org/show_bug.cgi?id=750149

docs/libs/gstreamer-libs-sections.txt
libs/gst/base/gstqueuearray.c
libs/gst/base/gstqueuearray.h
win32/common/libgstbase.def

index 97143d8..52319e5 100644 (file)
@@ -860,6 +860,11 @@ gst_queue_array_push_tail
 gst_queue_array_is_empty
 gst_queue_array_drop_element
 gst_queue_array_find
+gst_queue_array_new_for_struct
+gst_queue_array_push_tail_struct
+gst_queue_array_peek_head_struct
+gst_queue_array_pop_head_struct
+gst_queue_array_drop_struct
 </SECTION>
 
 # net
index 5a69b69..3a64446 100644 (file)
@@ -1,5 +1,6 @@
 /* GStreamer
  * Copyright (C) 2009 Edward Hervey <bilboed@bilboed.com>
+ * Copyright (C) 2015 Tim-Philipp Müller <tim@centricular.com>
  *
  * gstqueuearray.c:
  *
 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: (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;
   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)
@@ -85,6 +113,36 @@ gst_queue_array_free (GstQueueArray * array)
 }
 
 /**
+ * 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
  *
@@ -103,7 +161,8 @@ gst_queue_array_pop_head (GstQueueArray * array)
   /* 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 +170,32 @@ 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)
+{
+  /* 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
@@ -127,7 +208,77 @@ gst_queue_array_peek_head (GstQueueArray * array)
   /* 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++;
 }
 
 /**
@@ -143,40 +294,10 @@ void
 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 = 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++;
@@ -198,32 +319,40 @@ gst_queue_array_is_empty (GstQueueArray * array)
   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 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) {
@@ -231,7 +360,7 @@ gst_queue_array_drop_element (GstQueueArray * array, guint idx)
     array->head++;
     array->head %= array->size;
     array->length--;
-    return element;
+    return TRUE;
   }
 
   /* simple case idx == last item */
@@ -239,19 +368,20 @@ gst_queue_array_drop_element (GstQueueArray * array, guint 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));
+    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 */
@@ -259,28 +389,51 @@ gst_queue_array_drop_element (GstQueueArray * array, guint idx)
 
   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) {
     /* 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_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;
 }
 
 /**
@@ -305,17 +458,28 @@ 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->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;
     }
   }
index bfe89e7..7c94462 100644 (file)
@@ -50,4 +50,20 @@ guint           gst_queue_array_find (GstQueueArray * array,
 
 guint           gst_queue_array_get_length (GstQueueArray * array);
 
+/* Functions for use with structures */
+
+GstQueueArray * gst_queue_array_new_for_struct (gsize struct_size,
+                                                guint initial_size);
+
+void            gst_queue_array_push_tail_struct (GstQueueArray * array,
+                                                  gpointer        p_struct);
+
+gpointer        gst_queue_array_pop_head_struct  (GstQueueArray * array);
+
+gpointer        gst_queue_array_peek_head_struct (GstQueueArray * array);
+
+gboolean        gst_queue_array_drop_struct      (GstQueueArray * array,
+                                                  guint           idx,
+                                                  gpointer        p_struct);
+
 #endif
index a5d156d..6a12e06 100644 (file)
@@ -282,14 +282,19 @@ EXPORTS
        gst_flow_combiner_update_pad_flow
        gst_push_src_get_type
        gst_queue_array_drop_element
+       gst_queue_array_drop_struct
        gst_queue_array_find
        gst_queue_array_free
        gst_queue_array_get_length
        gst_queue_array_is_empty
        gst_queue_array_new
+       gst_queue_array_new_for_struct
        gst_queue_array_peek_head
+       gst_queue_array_peek_head_struct
        gst_queue_array_pop_head
+       gst_queue_array_pop_head_struct
        gst_queue_array_push_tail
+       gst_queue_array_push_tail_struct
        gst_type_find_helper
        gst_type_find_helper_for_buffer
        gst_type_find_helper_for_data