[libdazzle] list-store: directly access GSequence from GtkListStore
- From: Christian Hergert <chergert src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libdazzle] list-store: directly access GSequence from GtkListStore
- Date: Wed, 5 Jul 2017 06:36:27 +0000 (UTC)
commit a20677accf9651d9cb4b1a2f5d30c29a4ec7895e
Author: Christian Hergert <chergert redhat com>
Date: Tue Jul 4 23:34:07 2017 -0700
list-store: directly access GSequence from GtkListStore
With knowledge of the GtkListStore implementation, we can
directly access the GSequenceIter in GtkTreeIter.user_data.
Doing so allows us to avoid traversing the GSequence treap
multiple times while inserting a value.
This is not guaranteed to do inserts exactly as a binary
search, as the GSequence approximates the mid-point based
on the sequence hierarchy.
src/util/dzl-gtk.c | 112 ++++++++++++++++++++++++++++++++++-------------
tests/meson.build | 6 +++
tests/test-list-store.c | 68 ++++++++++++++++++++++++++++
3 files changed, 155 insertions(+), 31 deletions(-)
---
diff --git a/src/util/dzl-gtk.c b/src/util/dzl-gtk.c
index 87c6fce..91d2b78 100644
--- a/src/util/dzl-gtk.c
+++ b/src/util/dzl-gtk.c
@@ -444,6 +444,42 @@ dzl_gtk_widget_mux_action_groups (GtkWidget *widget,
(GDestroyNotify) g_strfreev);
}
+static gboolean
+list_store_iter_middle (GtkListStore *store,
+ const GtkTreeIter *begin,
+ const GtkTreeIter *end,
+ GtkTreeIter *middle)
+{
+ g_assert (store != NULL);
+ g_assert (begin != NULL);
+ g_assert (end != NULL);
+ g_assert (middle != NULL);
+ g_assert (middle->stamp == begin->stamp);
+ g_assert (middle->stamp == end->stamp);
+
+ /*
+ * middle MUST ALREADY BE VALID as it saves us some copying
+ * as well as just makes things easier when binary searching.
+ */
+
+ middle->user_data = g_sequence_range_get_midpoint (begin->user_data, end->user_data);
+
+ if (g_sequence_iter_is_end (middle->user_data))
+ {
+ middle->stamp = 0;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static inline gboolean
+list_store_iter_equal (const GtkTreeIter *a,
+ const GtkTreeIter *b)
+{
+ return a->user_data == b->user_data;
+}
+
/**
* dzl_gtk_list_store_insert_sorted:
* @store: A #GtkListStore
@@ -459,9 +495,9 @@ dzl_gtk_widget_mux_action_groups (GtkWidget *widget,
* @compare_column must be the index of a column that is a %G_TYPE_POINTER,
* %G_TYPE_BOXED or %G_TYPE_OBJECT based column.
*
- * @compare_func will be called with the column data as the first
- * parameter and @key as the second parameter. The final parameter will
- * be the @compare_data passed to this function.
+ * @compare_func will be called with @key as the first parameter and the
+ * value from the #GtkListStore row as the second parameter. The third and
+ * final parameter is @compare_data.
*
* Since: 3.26
*/
@@ -476,11 +512,12 @@ dzl_gtk_list_store_insert_sorted (GtkListStore *store,
GValue value = G_VALUE_INIT;
gpointer (*get_func) (const GValue *) = NULL;
GtkTreeModel *model = (GtkTreeModel *)store;
+ GtkTreeIter begin;
+ GtkTreeIter end;
+ GtkTreeIter middle;
+ guint n_children;
+ gint cmpval = 0;
GType type;
- gint left;
- gint right;
- gint middle;
- gint cmpval;
g_return_if_fail (GTK_IS_LIST_STORE (store));
g_return_if_fail (GTK_IS_LIST_STORE (model));
@@ -504,40 +541,53 @@ dzl_gtk_list_store_insert_sorted (GtkListStore *store,
return;
}
- cmpval = 1;
- left = 0;
- middle = 0;
- right = gtk_tree_model_iter_n_children (model, NULL) - 1;
-
- /* Binary search to locate the target position */
-
- while (left <= right)
+ /* Try to get the first iter instead of calling n_children to
+ * avoid walking the GSequence all the way to the right. If this
+ * matches, we know there are some children.
+ */
+ if (!gtk_tree_model_get_iter_first (model, &begin))
{
- GtkTreeIter cur;
-
- middle = (left + right) / 2;
+ gtk_list_store_append (store, iter);
+ return;
+ }
- gtk_tree_model_iter_nth_child (model, &cur, NULL, middle);
- gtk_tree_model_get_value (model, &cur, compare_column, &value);
+ n_children = gtk_tree_model_iter_n_children (model, NULL);
+ if (!gtk_tree_model_iter_nth_child (model, &end, NULL, n_children - 1))
+ g_assert_not_reached ();
- cmpval = compare_func (get_func (&value), key, compare_data);
+ middle = begin;
+ while (list_store_iter_middle (store, &begin, &end, &middle))
+ {
+ gtk_tree_model_get_value (model, &middle, compare_column, &value);
+ cmpval = compare_func (key, get_func (&value), compare_data);
g_value_unset (&value);
+ if (cmpval == 0 || list_store_iter_equal (&begin, &end))
+ break;
+
if (cmpval < 0)
- left = middle + 1;
+ {
+ end = middle;
+
+ if (!list_store_iter_equal (&begin, &end) &&
+ !gtk_tree_model_iter_previous (model, &end))
+ break;
+ }
else if (cmpval > 0)
- right = middle - 1;
+ {
+ begin = middle;
+
+ if (!list_store_iter_equal (&begin, &end) &&
+ !gtk_tree_model_iter_next (model, &begin))
+ break;
+ }
else
- break;
+ g_assert_not_reached ();
}
- /*
- * If we binary searched and middle was compared previous
- * to our new position, advance one position.
- */
if (cmpval < 0)
- middle++;
-
- gtk_list_store_insert (store, iter, middle);
+ gtk_list_store_insert_before (store, iter, &middle);
+ else
+ gtk_list_store_insert_after (store, iter, &middle);
}
diff --git a/tests/meson.build b/tests/meson.build
index db501d4..347c0a4 100644
--- a/tests/meson.build
+++ b/tests/meson.build
@@ -286,4 +286,10 @@ test_counters_window = executable('test-counters-window', 'test-counters-window.
dependencies: libdazzle_deps + [libdazzle_dep],
)
+test_list_store = executable('test-list-store', 'test-list-store.c',
+ c_args: test_cflags,
+ link_args: test_link_args,
+ dependencies: libdazzle_deps + [libdazzle_dep],
+)
+
endif
diff --git a/tests/test-list-store.c b/tests/test-list-store.c
new file mode 100644
index 0000000..06ad64e
--- /dev/null
+++ b/tests/test-list-store.c
@@ -0,0 +1,68 @@
+#include <dazzle.h>
+#include <stdlib.h>
+
+static gint
+compare_direct (gconstpointer a,
+ gconstpointer b,
+ gpointer data)
+{
+ if (a < b)
+ return -1;
+ else if (a > b)
+ return 1;
+ return 0;
+}
+
+#define assert_cmpptr(a,eq,b) \
+ G_STMT_START { \
+ if (!((a) eq (b))) { \
+ g_error (#a "(%p) " #eq " " #b "(%p)\n", (a), (b)); \
+ } \
+ } G_STMT_END
+
+static void
+test_insert_sorted (void)
+{
+ GtkListStore *store = gtk_list_store_new (1, G_TYPE_POINTER);
+ GtkTreeIter iter;
+ gboolean r;
+ gpointer last = NULL;
+ guint count = 0;
+
+ for (guint i = 0; i < 1000; i++)
+ {
+ gpointer value = GINT_TO_POINTER (rand ());
+
+ dzl_gtk_list_store_insert_sorted (store, &iter, value, 0, compare_direct, NULL);
+ gtk_list_store_set (store, &iter, 0, value, -1);
+ }
+
+ r = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (store), &iter);
+ g_assert_cmpint (r, ==, TRUE);
+
+ do
+ {
+ gpointer value = NULL;
+
+ gtk_tree_model_get (GTK_TREE_MODEL (store), &iter, 0, &value, -1);
+ assert_cmpptr (value, >=, last);
+ last = value;
+
+ count++;
+ }
+ while (gtk_tree_model_iter_next (GTK_TREE_MODEL (store), &iter));
+
+ g_assert_cmpint (count, ==, 1000);
+
+ g_object_unref (store);
+}
+
+gint
+main (gint argc,
+ gchar *argv[])
+{
+ gtk_init (&argc, &argv);
+ g_test_init (&argc, &argv, NULL);
+ g_test_add_func ("/Dazzle/GtkListStore/insert_sorted", test_insert_sorted);
+ return g_test_run ();
+}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]