From 2f7dce36e2612009d3a7a0bc15dbcca6c6af94fb Mon Sep 17 00:00:00 2001 From: Georges Basile Stavracas Neto Date: Sun, 3 May 2026 13:05:31 -0300 Subject: [PATCH] core/event-list: Use gdkarrayimpl.c as backing store GSequence is useful and provides us with a convenient API, however, it pretty much guarantees cache misses when iterating, lots of pointer chasing, and scattered memory. gdkarrayimpl.c works around all of that, at the cost of more expensive additions and removals. But because we do a lot more lookups compared to edits, there should still be benefits to it. Change the backing store to be based on gdkarrayimpl.c. Part-of: --- src/core/gcal-event-list.c | 142 ++++++++++++++----------------------- 1 file changed, 55 insertions(+), 87 deletions(-) diff --git a/src/core/gcal-event-list.c b/src/core/gcal-event-list.c index 021408fc..17f56a66 100644 --- a/src/core/gcal-event-list.c +++ b/src/core/gcal-event-list.c @@ -26,19 +26,19 @@ #include +#define GDK_ARRAY_TYPE_NAME GcalEventArray +#define GDK_ARRAY_NAME gcal_event_array +#define GDK_ARRAY_ELEMENT_TYPE GcalEvent* +#define GDK_ARRAY_PREALLOC 50 +#define GDK_ARRAY_FREE_FUNC g_object_unref +#include "gdkarrayimpl.c" + struct _GcalEventList { GObject parent_instance; - GSequence *events; - GHashTable *event_to_iter; - GHashTable *uid_to_iter; - guint n_events; - - /* cache */ - guint last_position; - GSequenceIter *last_iter; - gboolean last_position_valid; + GcalEventArray event_array; + GHashTable *events; }; static void g_list_model_interface_init (GListModelInterface *iface); @@ -47,28 +47,6 @@ G_DEFINE_FINAL_TYPE_WITH_CODE (GcalEventList, gcal_event_list, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, g_list_model_interface_init)) -/* - * Auxiliary functions - */ - -static void -gcal_event_list_items_changed (GcalEventList *self, - guint position, - guint removed, - guint added) -{ - /* check if the iter cache may have been invalidated */ - if (position <= self->last_position) - { - self->last_iter = NULL; - self->last_position = 0; - self->last_position_valid = FALSE; - } - - g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added); -} - - /* * GListModel interface */ @@ -86,7 +64,7 @@ gcal_event_list_get_n_items (GListModel *model) g_assert (GCAL_IS_EVENT_LIST (self)); - return g_hash_table_size (self->event_to_iter); + return gcal_event_array_get_size (&self->event_array); } static gpointer @@ -94,31 +72,13 @@ gcal_event_list_get_item (GListModel *model, guint position) { GcalEventList *self = (GcalEventList *) model; - GSequenceIter *it = NULL; g_assert (GCAL_IS_EVENT_LIST (self)); - if (self->last_position_valid) - { - if (position < G_MAXUINT && self->last_position == position + 1) - it = g_sequence_iter_prev (self->last_iter); - else if (position > 0 && self->last_position == position - 1) - it = g_sequence_iter_next (self->last_iter); - else if (self->last_position == position) - it = self->last_iter; - } - - if (it == NULL) - it = g_sequence_get_iter_at_pos (self->events, position); - - self->last_iter = it; - self->last_position = position; - self->last_position_valid = TRUE; - - if (g_sequence_iter_is_end (it)) - return NULL; + if (position < gcal_event_array_get_size (&self->event_array)) + return g_object_ref (gcal_event_array_get (&self->event_array, position)); else - return g_object_ref (g_sequence_get (it)); + return NULL; } static void @@ -139,8 +99,8 @@ gcal_event_list_finalize (GObject *object) { GcalEventList *self = (GcalEventList *)object; - g_clear_pointer (&self->event_to_iter, g_hash_table_destroy); - g_clear_pointer (&self->events, g_sequence_free); + gcal_event_array_clear (&self->event_array); + g_clear_pointer (&self->events, g_hash_table_destroy); G_OBJECT_CLASS (gcal_event_list_parent_class)->finalize (object); } @@ -156,8 +116,8 @@ gcal_event_list_class_init (GcalEventListClass *klass) static void gcal_event_list_init (GcalEventList *self) { - self->events = g_sequence_new (g_object_unref); - self->event_to_iter = g_hash_table_new (g_direct_hash, g_direct_equal); + gcal_event_array_init (&self->event_array); + self->events = g_hash_table_new (g_direct_hash, g_direct_equal); } /** @@ -210,23 +170,20 @@ gcal_event_list_add_events (GcalEventList *self, g_assert (GCAL_IS_EVENT_LIST (self)); g_assert (events != NULL); - position = g_hash_table_size (self->event_to_iter); + position = gcal_event_array_get_size (&self->event_array); for (gsize i = 0; events[i]; i++) { - GSequenceIter *iter; - - if (g_hash_table_contains (self->event_to_iter, events[i])) + if (g_hash_table_contains (self->events, events[i])) continue; - iter = g_sequence_append (self->events, g_object_ref (events[i])); - - g_hash_table_insert (self->event_to_iter, events[i], iter); + gcal_event_array_append (&self->event_array, g_object_ref (events[i])); + g_hash_table_add (self->events, events[i]); n_added++; } if (n_added > 0) - gcal_event_list_items_changed (self, position, 0, n_added); + g_list_model_items_changed (G_LIST_MODEL (self), position, 0, n_added); } /** @@ -261,35 +218,47 @@ void gcal_event_list_remove_events (GcalEventList *self, GcalEvent **events) { + g_autoptr (GHashTable) contained_events = NULL; g_autoptr (GtkBitset) bitset = NULL; - g_autoptr (GPtrArray) iters = NULL; + GtkBitsetIter iter; g_assert (GCAL_IS_EVENT_LIST (self)); g_assert (events != NULL); bitset = gtk_bitset_new_empty (); - iters = g_ptr_array_new (); + contained_events = g_hash_table_new (g_direct_hash, g_direct_equal); - for (gsize i = 0; events[i]; i++) + for (size_t i = 0; events[i]; i++) { - GSequenceIter *iter = g_hash_table_lookup (self->event_to_iter, events[i]); - - if (!iter) - continue; - - g_ptr_array_add (iters, iter); - gtk_bitset_add (bitset, g_sequence_iter_get_position (iter)); - - g_hash_table_remove (self->event_to_iter, events[i]); + if (g_hash_table_contains (self->events, events[i])) + g_hash_table_add (contained_events, events[i]); } - for (gsize i = 0; i < iters->len; i++) + for (size_t i = 0; i < gcal_event_array_get_size (&self->event_array); i++) { - GSequenceIter *iter = g_ptr_array_index (iters, i); + GcalEvent *event = gcal_event_array_get (&self->event_array, i); - g_assert (iter != NULL); + if (g_hash_table_contains (contained_events, event)) + { + g_hash_table_remove (contained_events, event); + g_hash_table_remove (self->events, event); + gtk_bitset_add (bitset, i); + } - g_sequence_remove (iter); + if (g_hash_table_size (contained_events) == 0) + break; + } + + for (gtk_bitset_iter_init_last (&iter, bitset, NULL); + gtk_bitset_iter_is_valid (&iter); + gtk_bitset_iter_previous (&iter, NULL)) + { + gcal_event_array_splice (&self->event_array, + gtk_bitset_iter_get_value (&iter), + 1, + FALSE, + NULL, + 0); } if (!gtk_bitset_is_empty (bitset)) @@ -299,7 +268,7 @@ gcal_event_list_remove_events (GcalEventList *self, guint range_size = last - first + 1; guint n_removed = gtk_bitset_get_size_in_range (bitset, first, last); - gcal_event_list_items_changed (self, first, range_size, range_size - n_removed); + g_list_model_items_changed (G_LIST_MODEL (self), first, range_size, range_size - n_removed); } } @@ -315,12 +284,11 @@ gcal_event_list_remove_all_events (GcalEventList *self) g_assert (GCAL_IS_EVENT_LIST (self)); - n_removed = g_hash_table_size (self->event_to_iter); + n_removed = gcal_event_array_get_size (&self->event_array); - g_hash_table_remove_all (self->event_to_iter); - g_sequence_remove_range (g_sequence_get_begin_iter (self->events), - g_sequence_get_end_iter (self->events)); + g_hash_table_remove_all (self->events); + gcal_event_array_clear (&self->event_array); if (n_removed > 0) - gcal_event_list_items_changed (self, 0, n_removed, 0); + g_list_model_items_changed (G_LIST_MODEL (self), 0, n_removed, 0); }