API: Add gst_util_array_binary_search() for binary searchs on a sorted array
authorSebastian Dröge <sebastian.droege@collabora.co.uk>
Mon, 2 Mar 2009 15:17:45 +0000 (16:17 +0100)
committerSebastian Dröge <sebastian.droege@collabora.co.uk>
Mon, 2 Mar 2009 15:17:45 +0000 (16:17 +0100)
This will be mostly useful in all elements that have some kind of internal
seek/index table. Currently almost all of them (or even all of them)
are using a linear search although the used array is already sorted,
wasting some CPU time without good reason.

Fixes bug #573623.

docs/gst/gstreamer-sections.txt
gst/gst.c
gst/gstutils.c
gst/gstutils.h
tests/check/gst/gstutils.c
win32/common/libgstreamer.def

index c9ebe19..1bc5655 100644 (file)
@@ -2363,10 +2363,14 @@ gst_util_seqnum_compare
 gst_util_set_object_arg
 gst_util_set_value_from_string
 gst_util_get_timestamp
+GstSearchMode
+gst_util_array_binary_search
 <SUBSECTION Private>
 GST_HAVE_UNALIGNED_ACCESS
 gst_util_guint64_to_gdouble
 gst_util_gdouble_to_guint64
+GST_TYPE_SEARCH_MODE
+gst_search_mode_get_type
 </SECTION>
 
 <SECTION>
index d6fa0d4..4085e3d 100644 (file)
--- a/gst/gst.c
+++ b/gst/gst.c
@@ -1073,6 +1073,7 @@ init_post (GOptionContext * context, GOptionGroup * group, gpointer data,
   g_type_class_ref (gst_uri_type_get_type ());
   g_type_class_ref (gst_parse_error_get_type ());
   g_type_class_ref (gst_parse_flags_get_type ());
+  g_type_class_ref (gst_search_mode_get_type ());
 
   gst_structure_get_type ();
   _gst_value_initialize ();
index 7c02f2a..a5ab233 100644 (file)
@@ -3624,3 +3624,91 @@ gst_util_get_timestamp (void)
   return GST_TIMEVAL_TO_TIME (now);
 #endif
 }
+
+/**
+ * gst_util_array_binary_search:
+ * @array: the sorted input array
+ * @num_elements: number of elements in the array
+ * @element_size: size of every element in bytes
+ * @search_func: function to compare two elements, @search_data will always be passed as second argument
+ * @mode: search mode that should be used
+ * @search_data: element that should be found
+ * @user_data: data to pass to @search_func
+ *
+ * Searches inside @array for @search_data by using the comparison function
+ * @search_func. @array must be sorted ascending.
+ *
+ * As @search_data is always passed as second argument to @search_func it's
+ * not required that @search_data has the same type as the array elements.
+ *
+ * The complexity of this search function is O(log (num_elements)).
+ *
+ * Returns: The address of the found element or %NULL if nothing was found
+ *
+ * Since: 0.10.23
+ */
+gpointer
+gst_util_array_binary_search (gpointer array, guint num_elements,
+    gsize element_size, GCompareDataFunc search_func, GstSearchMode mode,
+    gconstpointer search_data, gpointer user_data)
+{
+  glong left = 0, right = num_elements - 1, m;
+  gint ret;
+  guint8 *data = (guint8 *) array;
+
+  g_return_val_if_fail (array != NULL, NULL);
+  g_return_val_if_fail (element_size > 0, NULL);
+  g_return_val_if_fail (search_func != NULL, NULL);
+
+  /* 0. No elements => return NULL */
+  if (num_elements == 0)
+    return NULL;
+
+  /* 1. If search_data is before the 0th element return the 0th element */
+  ret = search_func (data, search_data, user_data);
+  if ((ret >= 0 && mode == GST_SEARCH_MODE_AFTER) || ret == 0)
+    return data;
+  else if (ret > 0)
+    return NULL;
+
+  /* 2. If search_data is after the last element return the last element */
+  ret =
+      search_func (data + (num_elements - 1) * element_size, search_data,
+      user_data);
+  if ((ret <= 0 && mode == GST_SEARCH_MODE_BEFORE) || ret == 0)
+    return data + (num_elements - 1) * element_size;
+  else if (ret < 0)
+    return NULL;
+
+  /* 3. else binary search */
+  while (TRUE) {
+    m = left + (right - left) / 2;
+
+    ret = search_func (data + m * element_size, search_data, user_data);
+
+    if (ret == 0) {
+      return data + m * element_size;
+    } else if (ret < 0) {
+      left = m + 1;
+    } else {
+      right = m - 1;
+    }
+
+    /* No exact match found */
+    if (right < left) {
+      if (mode == GST_SEARCH_MODE_EXACT) {
+        return NULL;
+      } else if (mode == GST_SEARCH_MODE_AFTER) {
+        if (ret < 0)
+          return (m < num_elements) ? data + (m + 1) * element_size : NULL;
+        else
+          return data + m * element_size;
+      } else {
+        if (ret < 0)
+          return data + m * element_size;
+        else
+          return (m > 0) ? data + (m - 1) * element_size : NULL;
+      }
+    }
+  }
+}
index 9a434bf..e2f5301 100644 (file)
@@ -1134,6 +1134,24 @@ GstElement *            gst_parse_bin_from_description_full (const gchar     * b
 
 GstClockTime gst_util_get_timestamp (void);
 
+/**
+ * GstSearchMode:
+ * @GST_SEARCH_MODE_EXACT : Only search for exact matches.
+ * @GST_SEARCH_MODE_BEFORE: Search for an exact match or the element just before.
+ * @GST_SEARCH_MODE_AFTER : Search for an exact match or the element just after.
+ *
+ * The different search modes.
+ *
+ * Since: 0.10.23
+ */
+typedef enum {
+  GST_SEARCH_MODE_EXACT = 0,
+  GST_SEARCH_MODE_BEFORE,
+  GST_SEARCH_MODE_AFTER
+} GstSearchMode;
+
+gpointer gst_util_array_binary_search (gpointer array, guint num_elements, gsize element_size, GCompareDataFunc search_func, GstSearchMode mode, gconstpointer search_data, gpointer user_data);
+
 G_END_DECLS
 
 #endif /* __GST_UTILS_H__ */
index 3b619a0..a98b112 100644 (file)
@@ -610,6 +610,108 @@ GST_START_TEST (test_set_value_from_string)
 
 GST_END_TEST;
 
+static gint
+_binary_search_compare (guint32 * a, guint32 * b)
+{
+  return *a - *b;
+}
+
+GST_START_TEST (test_binary_search)
+{
+  guint32 data[257];
+  guint32 *match;
+  guint32 search_element = 121 * 2;
+  guint i;
+
+  for (i = 0; i < 257; i++)
+    data[i] = (i + 1) * 2;
+
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_EXACT,
+      &search_element, NULL);
+  fail_unless (match != NULL);
+  fail_unless_equals_int (match - data, 120);
+
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_BEFORE,
+      &search_element, NULL);
+  fail_unless (match != NULL);
+  fail_unless_equals_int (match - data, 120);
+
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_AFTER,
+      &search_element, NULL);
+  fail_unless (match != NULL);
+  fail_unless_equals_int (match - data, 120);
+
+  search_element = 0;
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_EXACT,
+      &search_element, NULL);
+  fail_unless (match == NULL);
+
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_AFTER,
+      &search_element, NULL);
+  fail_unless (match != NULL);
+  fail_unless_equals_int (match - data, 0);
+
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_BEFORE,
+      &search_element, NULL);
+  fail_unless (match == NULL);
+
+  search_element = 1000;
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_EXACT,
+      &search_element, NULL);
+  fail_unless (match == NULL);
+
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_AFTER,
+      &search_element, NULL);
+  fail_unless (match == NULL);
+
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_BEFORE,
+      &search_element, NULL);
+  fail_unless (match != NULL);
+  fail_unless_equals_int (match - data, 256);
+
+  search_element = 121 * 2 - 1;
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_EXACT,
+      &search_element, NULL);
+  fail_unless (match == NULL);
+
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_AFTER,
+      &search_element, NULL);
+  fail_unless (match != NULL);
+  fail_unless_equals_int (match - data, 120);
+
+  match =
+      (guint32 *) gst_util_array_binary_search (data, 257, sizeof (guint32),
+      (GCompareDataFunc) _binary_search_compare, GST_SEARCH_MODE_BEFORE,
+      &search_element, NULL);
+  fail_unless (match != NULL);
+  fail_unless_equals_int (match - data, 119);
+
+}
+
+GST_END_TEST;
+
 static Suite *
 gst_utils_suite (void)
 {
@@ -630,6 +732,7 @@ gst_utils_suite (void)
   tcase_add_test (tc_chain, test_element_found_tags);
   tcase_add_test (tc_chain, test_element_unlink);
   tcase_add_test (tc_chain, test_set_value_from_string);
+  tcase_add_test (tc_chain, test_binary_search);
   return s;
 }
 
index 80b56b4..274c934 100644 (file)
@@ -784,6 +784,7 @@ EXPORTS
        gst_registry_xml_write_cache
        gst_resource_error_get_type
        gst_resource_error_quark
+       gst_search_mode_get_type
        gst_seek_flags_get_type
        gst_seek_type_get_type
        gst_segment_clip
@@ -970,6 +971,7 @@ EXPORTS
        gst_uri_protocol_is_supported
        gst_uri_protocol_is_valid
        gst_uri_type_get_type
+       gst_util_array_binary_search
        gst_util_dump_mem
        gst_util_gdouble_to_guint64
        gst_util_get_timestamp