stack: Use a pointer array for children
authorLukáš Tyrychtr <lukastyrychtr@gmail.com>
Fri, 16 Sep 2022 14:29:53 +0000 (16:29 +0200)
committerEmmanuele Bassi <ebassi@gnome.org>
Fri, 3 Feb 2023 10:49:17 +0000 (11:49 +0100)
Reduce our dependency on linked lists; pointer arrays are better at
cache locality.

Additionally, we can avoid quadratic behaviors when finding a child at a
given index.

gtk/gtkstack.c

index 2d2de8990dfdd56b926b53a75389e98bc39175ca..b43950725ec97ec4969dd8aed2091b6ea359fbea 100644 (file)
@@ -139,7 +139,7 @@ struct _GtkStackClass {
 };
 
 typedef struct {
-  GList *children;
+  GPtrArray *children;
 
   GtkStackPage *visible_child;
 
@@ -569,7 +569,7 @@ gtk_stack_pages_get_n_items (GListModel *model)
   GtkStackPages *pages = GTK_STACK_PAGES (model);
   GtkStackPrivate *priv = gtk_stack_get_instance_private (pages->stack);
 
-  return g_list_length (priv->children);
+  return priv->children->len;
 }
 
 static gpointer
@@ -580,11 +580,12 @@ gtk_stack_pages_get_item (GListModel *model,
   GtkStackPrivate *priv = gtk_stack_get_instance_private (pages->stack);
   GtkStackPage *page;
 
-  page = g_list_nth_data (priv->children, position);
 
-  if (!page)
+  if (position > priv->children->len - 1)
     return NULL;
 
+  page = g_ptr_array_index (priv->children, position);
+
   return g_object_ref (page);
 }
 
@@ -604,9 +605,12 @@ gtk_stack_pages_is_selected (GtkSelectionModel *model,
   GtkStackPrivate *priv = gtk_stack_get_instance_private (pages->stack);
   GtkStackPage *page;
 
-  page = g_list_nth_data (priv->children, position);
+  if (position > priv->children->len - 1)
+    return FALSE;
+
+  page = g_ptr_array_index (priv->children, position);
 
-  return page && page == priv->visible_child;
+  return page == priv->visible_child;
 }
 
 static void set_visible_child (GtkStack               *stack,
@@ -623,7 +627,7 @@ gtk_stack_pages_select_item (GtkSelectionModel *model,
   GtkStackPrivate *priv = gtk_stack_get_instance_private (pages->stack);
   GtkStackPage *page;
 
-  page = g_list_nth_data (priv->children, position);
+  page = g_ptr_array_index (priv->children, position);
 
   set_visible_child (pages->stack, page, priv->transition_type, priv->transition_duration);
 
@@ -657,7 +661,7 @@ gtk_stack_pages_get_property (GObject    *object,
 
     case PAGES_PROP_N_ITEMS:
       g_value_set_uint (value, gtk_stack_pages_get_n_items (G_LIST_MODEL (self)));
-      break;
+      break;  
 
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -768,7 +772,7 @@ gtk_stack_accessible_get_child_at_index (GtkAccessible *accessible, guint index)
 {
   GtkStack *stack = GTK_STACK (accessible);
   GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
-  GtkStackPage *page = g_list_nth_data (priv->children, index);
+  GtkStackPage *page = g_ptr_array_index (priv->children, index);
   return GTK_ACCESSIBLE (page);
 }
 
@@ -788,7 +792,7 @@ gtk_stack_dispose (GObject *obj)
   GtkStack *stack = GTK_STACK (obj);
   GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
   GtkWidget *child;
-  guint n_pages = g_list_length (priv->children);
+  guint n_pages = priv->children->len;
 
   while ((child = gtk_widget_get_first_child (GTK_WIDGET (stack))))
     stack_remove (stack, child, TRUE);
@@ -813,6 +817,8 @@ gtk_stack_finalize (GObject *obj)
 
   gtk_stack_unschedule_ticks (stack);
 
+  g_ptr_array_free (priv->children, TRUE);
+
   G_OBJECT_CLASS (gtk_stack_parent_class)->finalize (obj);
 }
 
@@ -1028,11 +1034,11 @@ find_child_info_for_widget (GtkStack  *stack,
 {
   GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
   GtkStackPage *info;
-  GList *l;
+  guint idx;
 
-  for (l = priv->children; l != NULL; l = l->next)
+  for (idx = 0; idx < priv->children->len; idx++)
     {
-      info = l->data;
+      info = g_ptr_array_index (priv->children, idx);
       if (info->widget == child)
         return info;
     }
@@ -1347,7 +1353,7 @@ set_visible_child (GtkStack               *stack,
   GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
   GtkStackPage *info;
   GtkWidget *widget = GTK_WIDGET (stack);
-  GList *l;
+  guint idx;
   GtkWidget *focus;
   gboolean contains_focus = FALSE;
   guint old_pos = GTK_INVALID_LIST_POSITION;
@@ -1362,9 +1368,9 @@ set_visible_child (GtkStack               *stack,
   /* If none, pick first visible */
   if (child_info == NULL)
     {
-      for (l = priv->children; l != NULL; l = l->next)
+      for (idx = 0; idx < priv->children->len; idx++)
         {
-          info = l->data;
+          info = g_ptr_array_index (priv->children, idx);
           if (gtk_widget_get_visible (info->widget))
             {
               child_info = info;
@@ -1379,9 +1385,9 @@ set_visible_child (GtkStack               *stack,
   if (priv->pages)
     {
       guint position;
-      for (l = priv->children, position = 0; l != NULL; l = l->next, position++)
+      for (idx = 0, position = 0; idx < priv->children->len; idx++, position++)
         {
-          info = l->data;
+          info = g_ptr_array_index (priv->children, idx);
           if (info == priv->visible_child)
             old_pos = position;
           else if (info == child_info)
@@ -1449,14 +1455,14 @@ set_visible_child (GtkStack               *stack,
   else if (is_direction_dependent_transition (transition_type))
     {
       gboolean i_first = FALSE;
-      for (l = priv->children; l != NULL; l = l->next)
+      for (idx = 0; idx < priv->children->len; idx++)
         {
-         if (child_info == l->data)
+         if (child_info == g_ptr_array_index (priv->children, idx))
            {
              i_first = TRUE;
              break;
            }
-         if (priv->last_visible_child == l->data)
+         if (priv->last_visible_child == g_ptr_array_index (priv->children, idx))
            break;
         }
 
@@ -1625,15 +1631,15 @@ gtk_stack_add_page (GtkStack     *stack,
                     GtkStackPage *child_info)
 {
   GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
-  GList *l;
+  guint idx;
 
   g_return_if_fail (child_info->widget != NULL);
 
   if (child_info->name)
     {
-      for (l = priv->children; l != NULL; l = l->next)
+      for (idx = 0; idx < priv->children->len; idx++)
         {
-          GtkStackPage *info = l->data;
+          GtkStackPage *info = g_ptr_array_index (priv->children, idx);
           if (info->name &&
               g_strcmp0 (info->name, child_info->name) == 0)
             {
@@ -1643,14 +1649,14 @@ gtk_stack_add_page (GtkStack     *stack,
         }
     }
 
-  priv->children = g_list_append (priv->children, g_object_ref (child_info));
+  g_ptr_array_add (priv->children, g_object_ref (child_info));
 
   gtk_widget_set_child_visible (child_info->widget, FALSE);
   gtk_widget_set_parent (child_info->widget, GTK_WIDGET (stack));
 
   if (priv->pages)
     {
-      g_list_model_items_changed (G_LIST_MODEL (priv->pages), g_list_length (priv->children) - 1, 0, 1);
+      g_list_model_items_changed (G_LIST_MODEL (priv->pages), priv->children->len - 1, 0, 1);
       g_object_notify_by_pspec (G_OBJECT (priv->pages), pages_properties[PAGES_PROP_N_ITEMS]);
     }
 
@@ -1694,7 +1700,7 @@ stack_remove (GtkStack  *stack,
 
   g_clear_object (&child_info->widget);
 
-  priv->children = g_list_remove (priv->children, child_info);
+    g_ptr_array_remove (priv->children, child_info);
 
   g_object_unref (child_info);
 
@@ -1716,16 +1722,15 @@ gtk_stack_remove (GtkStack  *stack,
                   GtkWidget *child)
 {
   GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
-  GList *l;
   guint position;
 
   g_return_if_fail (GTK_IS_STACK (stack));
   g_return_if_fail (GTK_IS_WIDGET (child));
   g_return_if_fail (gtk_widget_get_parent (child) == GTK_WIDGET (stack));
 
-  for (l = priv->children, position = 0; l; l = l->next, position++)
+  for (position = 0; position < priv->children->len; position++)
     {
-      GtkStackPage *page = l->data;
+      GtkStackPage *page = g_ptr_array_index (priv->children, position);
       if (page->widget == child)
         break;
     }
@@ -1773,14 +1778,14 @@ gtk_stack_get_child_by_name (GtkStack    *stack,
 {
   GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
   GtkStackPage *info;
-  GList *l;
+  guint idx;
 
   g_return_val_if_fail (GTK_IS_STACK (stack), NULL);
   g_return_val_if_fail (name != NULL, NULL);
 
-  for (l = priv->children; l != NULL; l = l->next)
+  for (idx = 0; idx < priv->children->len; idx++)
     {
-      info = l->data;
+      info = g_ptr_array_index (priv->children, idx);
       if (info->name && strcmp (info->name, name) == 0)
         return info->widget;
     }
@@ -2191,7 +2196,7 @@ gtk_stack_set_visible_child_full (GtkStack               *stack,
 {
   GtkStackPrivate *priv = gtk_stack_get_instance_private (stack);
   GtkStackPage *child_info, *info;
-  GList *l;
+  guint idx;
 
   g_return_if_fail (GTK_IS_STACK (stack));
 
@@ -2199,9 +2204,9 @@ gtk_stack_set_visible_child_full (GtkStack               *stack,
     return;
 
   child_info = NULL;
-  for (l = priv->children; l != NULL; l = l->next)
+  for (idx = 0; idx < priv->children->len; idx++)
     {
-      info = l->data;
+      info = g_ptr_array_index (priv->children, idx);
       if (info->name != NULL &&
           strcmp (info->name, name) == 0)
         {
@@ -2230,13 +2235,13 @@ gtk_stack_compute_expand (GtkWidget *widget,
   gboolean hexpand, vexpand;
   GtkStackPage *child_info;
   GtkWidget *child;
-  GList *l;
+  guint idx;
 
   hexpand = FALSE;
   vexpand = FALSE;
-  for (l = priv->children; l != NULL; l = l->next)
+  for (idx = 0; idx < priv->children->len; idx++)
     {
-      child_info = l->data;
+      child_info = g_ptr_array_index (priv->children, idx);
       child = child_info->widget;
 
       if (!hexpand &&
@@ -2668,14 +2673,14 @@ gtk_stack_measure (GtkWidget      *widget,
   GtkStackPage *child_info;
   GtkWidget *child;
   int child_min, child_nat;
-  GList *l;
+  guint idx;
 
   *minimum = 0;
   *natural = 0;
 
-  for (l = priv->children; l != NULL; l = l->next)
+  for (idx = 0; idx < priv->children->len; idx++)
     {
-      child_info = l->data;
+      child_info = g_ptr_array_index (priv->children, idx);
       child = child_info->widget;
 
       if (!priv->homogeneous[orientation] &&
@@ -2724,6 +2729,7 @@ gtk_stack_init (GtkStack *stack)
   priv->homogeneous[GTK_ORIENTATION_HORIZONTAL] = TRUE;
   priv->transition_duration = 200;
   priv->transition_type = GTK_STACK_TRANSITION_TYPE_NONE;
+  priv->children = g_ptr_array_new();
 }
 
 /**
@@ -2905,14 +2911,14 @@ gtk_stack_page_set_name (GtkStackPage *self,
       gtk_widget_get_parent (self->widget) &&
       GTK_IS_STACK (gtk_widget_get_parent (self->widget)))
     {
-      GList *l;
+      guint idx;
 
       stack = GTK_STACK (gtk_widget_get_parent (self->widget));
       priv = gtk_stack_get_instance_private (stack);
 
-      for (l = priv->children; l != NULL; l = l->next)
+      for (idx = 0; idx < priv->children->len; idx++)
         {
-          GtkStackPage *info2 = l->data;
+          GtkStackPage *info2 = g_ptr_array_index (priv->children, idx);
           if (self == info2)
             continue;