sorter: Introduce GtkSortKeys

GtkSortKeys is an immutable struct that can be used to manage "sort
keys" for items.

Sort keys are memory that is created specifically for sorting. Because
sorting involves lots of comparisons, it's a good idea to prepare the
data relevant for sorting in advance and sort on that data.

In measurements with a PropertyExpression on a string sorter, it's about
??? faster
This commit is contained in:
Benjamin Otte 2020-07-15 20:17:55 +02:00
parent 8c608e9c1c
commit e34c7e6796
6 changed files with 378 additions and 2 deletions

View File

@ -19,7 +19,7 @@
#include "config.h"
#include "gtksorter.h"
#include "gtksorterprivate.h"
#include "gtkintl.h"
#include "gtktypebuiltins.h"
@ -48,12 +48,26 @@
* and provide one's own sorter.
*/
typedef struct _GtkSorterPrivate GtkSorterPrivate;
typedef struct _GtkDefaultSortKeys GtkDefaultSortKeys;
struct _GtkSorterPrivate
{
GtkSortKeys *keys;
};
struct _GtkDefaultSortKeys
{
GtkSortKeys keys;
GtkSorter *sorter;
};
enum {
CHANGED,
LAST_SIGNAL
};
G_DEFINE_TYPE (GtkSorter, gtk_sorter, G_TYPE_OBJECT)
G_DEFINE_TYPE_WITH_PRIVATE (GtkSorter, gtk_sorter, G_TYPE_OBJECT)
static guint signals[LAST_SIGNAL] = { 0 };
@ -73,9 +87,24 @@ gtk_sorter_default_get_order (GtkSorter *self)
return GTK_SORTER_ORDER_PARTIAL;
}
static void
gtk_sorter_dispose (GObject *object)
{
GtkSorter *self = GTK_SORTER (object);
GtkSorterPrivate *priv = gtk_sorter_get_instance_private (self);
g_clear_pointer (&priv->keys, gtk_sort_keys_unref);
G_OBJECT_CLASS (gtk_sorter_parent_class)->dispose (object);
}
static void
gtk_sorter_class_init (GtkSorterClass *class)
{
GObjectClass *object_class = G_OBJECT_CLASS (class);
object_class->dispose = gtk_sorter_dispose;
class->compare = gtk_sorter_default_compare;
class->get_order = gtk_sorter_default_get_order;
@ -181,6 +210,98 @@ gtk_sorter_get_order (GtkSorter *self)
return GTK_SORTER_GET_CLASS (self)->get_order (self);
}
static int
gtk_default_sort_keys_compare (gconstpointer a,
gconstpointer b,
gpointer data)
{
GtkDefaultSortKeys *self = data;
gpointer *key_a = (gpointer *) a;
gpointer *key_b = (gpointer *) b;
return gtk_sorter_compare (self->sorter, *key_a, *key_b);
}
static void
gtk_default_sort_keys_free (GtkSortKeys *keys)
{
GtkDefaultSortKeys *self = (GtkDefaultSortKeys *) keys;
g_object_unref (self->sorter);
g_slice_free (GtkDefaultSortKeys, self);
}
static gboolean
gtk_default_sort_keys_is_compatible (GtkSortKeys *keys,
GtkSortKeys *other)
{
if (keys->klass != other->klass)
return FALSE;
return TRUE;
}
static void
gtk_default_sort_keys_init_key (GtkSortKeys *self,
gpointer item,
gpointer key_memory)
{
gpointer *key = (gpointer *) key_memory;
*key = g_object_ref (item);
}
static void
gtk_default_sort_keys_clear_key (GtkSortKeys *self,
gpointer key_memory)
{
gpointer *key = (gpointer *) key_memory;
g_object_unref (*key);
}
static const GtkSortKeysClass GTK_DEFAULT_SORT_KEYS_CLASS =
{
gtk_default_sort_keys_free,
gtk_default_sort_keys_compare,
gtk_default_sort_keys_is_compatible,
gtk_default_sort_keys_init_key,
gtk_default_sort_keys_clear_key,
};
/*<private>
* gtk_sorter_get_keys:
* @self: a #GtkSorter
*
* Gets a #GtkSortKeys that can be used as an alternative to
* @self for faster sorting.
*
* The sort keys can change every time #GtkSorter::changed is emitted.
* When the keys change, you should redo all comparisons with the new
* keys.
* When gtk_sort_keys_is_compatible() for the old and new keys returns
* %TRUE, you can reuse keys you generated previously.
*
* Returns: (transfer full): the sort keys to sort with
**/
GtkSortKeys *
gtk_sorter_get_keys (GtkSorter *self)
{
GtkSorterPrivate *priv = gtk_sorter_get_instance_private (self);
GtkDefaultSortKeys *fallback;
g_return_val_if_fail (GTK_IS_SORTER (self), NULL);
if (priv->keys)
return gtk_sort_keys_ref (priv->keys);
fallback = gtk_sort_keys_new (GtkDefaultSortKeys, &GTK_DEFAULT_SORT_KEYS_CLASS, sizeof (gpointer), sizeof (gpointer));
fallback->sorter = g_object_ref (self);
return (GtkSortKeys *) fallback;
}
/**
* gtk_sorter_changed:
* @self: a #GtkSorter
@ -205,3 +326,31 @@ gtk_sorter_changed (GtkSorter *self,
g_signal_emit (self, signals[CHANGED], 0, change);
}
/*<private>
* gtk_sorter_changed_with_keys
* @self: a #GtkSorter
* @change: How the sorter changed
* @keys: (not nullable) (transfer full): New keys to use
*
* Updates the sorter's keys to @keys and then calls gtk_sorter_changed().
* If you do not want to update the keys, call that function instead.
*
* This function should also be called in your_sorter_init() to initialize
* the keys to use with your sorter.
*/
void
gtk_sorter_changed_with_keys (GtkSorter *self,
GtkSorterChange change,
GtkSortKeys *keys)
{
GtkSorterPrivate *priv = gtk_sorter_get_instance_private (self);
g_return_if_fail (GTK_IS_SORTER (self));
g_return_if_fail (keys != NULL);
g_clear_pointer (&priv->keys, gtk_sort_keys_unref);
priv->keys = keys;
gtk_sorter_changed (self, change);
}

View File

@ -115,6 +115,7 @@ GDK_AVAILABLE_IN_ALL
void gtk_sorter_changed (GtkSorter *self,
GtkSorterChange change);
G_END_DECLS
#endif /* __GTK_SORTER_H__ */

33
gtk/gtksorterprivate.h Normal file
View File

@ -0,0 +1,33 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_SORTER_PRIVATE_H__
#define __GTK_SORTER_PRIVATE_H__
#include <gtk/gtksorter.h>
#include "gtk/gtksortkeysprivate.h"
GtkSortKeys * gtk_sorter_get_keys (GtkSorter *self);
void gtk_sorter_changed_with_keys (GtkSorter *self,
GtkSorterChange change,
GtkSortKeys *keys);
#endif /* __GTK_SORTER_PRIVATE_H__ */

97
gtk/gtksortkeys.c Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#include "config.h"
#include "gtksortkeysprivate.h"
#include "gtkcssstyleprivate.h"
#include "gtkstyleproviderprivate.h"
GtkSortKeys *
gtk_sort_keys_alloc (const GtkSortKeysClass *klass,
gsize size,
gsize key_size,
gsize key_align)
{
GtkSortKeys *self;
g_return_val_if_fail (key_align > 0, NULL);
self = g_slice_alloc0 (size);
self->klass = klass;
self->ref_count = 1;
self->key_size = key_size;
self->key_align = key_align;
return self;
}
GtkSortKeys *
gtk_sort_keys_ref (GtkSortKeys *self)
{
self->ref_count += 1;
return self;
}
void
gtk_sort_keys_unref (GtkSortKeys *self)
{
self->ref_count -= 1;
if (self->ref_count > 0)
return;
self->klass->free (self);
}
gsize
gtk_sort_keys_get_key_size (GtkSortKeys *self)
{
return self->key_size;
}
gsize
gtk_sort_keys_get_key_align (GtkSortKeys *self)
{
return self->key_align;
}
GCompareDataFunc
gtk_sort_keys_get_key_compare_func (GtkSortKeys *self)
{
return self->klass->key_compare;
}
gboolean
gtk_sort_keys_is_compatible (GtkSortKeys *self,
GtkSortKeys *other)
{
if (self == other)
return TRUE;
return self->klass->is_compatible (self, other);
}
gboolean
gtk_sort_keys_needs_clear_key (GtkSortKeys *self)
{
return self->klass->clear_key != NULL;
}

95
gtk/gtksortkeysprivate.h Normal file
View File

@ -0,0 +1,95 @@
/*
* Copyright © 2020 Benjamin Otte
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef __GTK_SORT_KEYS_PRIVATE_H__
#define __GTK_SORT_KEYS_PRIVATE_H__
#include <gdk/gdk.h>
#include <gtk/gtkenums.h>
#include <gtk/gtksorter.h>
typedef struct _GtkSortKeys GtkSortKeys;
typedef struct _GtkSortKeysClass GtkSortKeysClass;
struct _GtkSortKeys
{
const GtkSortKeysClass *klass;
gint ref_count;
gsize key_size;
gsize key_align; /* must be power of 2 */
};
struct _GtkSortKeysClass
{
void (* free) (GtkSortKeys *self);
GCompareDataFunc key_compare;
gboolean (* is_compatible) (GtkSortKeys *self,
GtkSortKeys *other);
void (* init_key) (GtkSortKeys *self,
gpointer item,
gpointer key_memory);
void (* clear_key) (GtkSortKeys *self,
gpointer key_memory);
};
GtkSortKeys * gtk_sort_keys_alloc (const GtkSortKeysClass *klass,
gsize size,
gsize key_size,
gsize key_align);
#define gtk_sort_keys_new(_name, _klass, _key_size, _key_align) \
((_name *) gtk_sort_keys_alloc ((_klass), sizeof (_name), (_key_size), (_key_align)))
GtkSortKeys * gtk_sort_keys_ref (GtkSortKeys *self);
void gtk_sort_keys_unref (GtkSortKeys *self);
gsize gtk_sort_keys_get_key_size (GtkSortKeys *self);
gsize gtk_sort_keys_get_key_align (GtkSortKeys *self);
GCompareDataFunc gtk_sort_keys_get_key_compare_func (GtkSortKeys *self);
gboolean gtk_sort_keys_is_compatible (GtkSortKeys *self,
GtkSortKeys *other);
gboolean gtk_sort_keys_needs_clear_key (GtkSortKeys *self);
#define GTK_SORT_KEYS_ALIGN(_size,_align) (((_size) + (_align) - 1) & ~((_align) - 1))
static inline int
gtk_sort_keys_compare (GtkSortKeys *self,
gconstpointer a,
gconstpointer b)
{
return self->klass->key_compare (a, b, self);
}
static inline void
gtk_sort_keys_init_key (GtkSortKeys *self,
gpointer item,
gpointer key_memory)
{
self->klass->init_key (self, item, key_memory);
}
static inline void
gtk_sort_keys_clear_key (GtkSortKeys *self,
gpointer key_memory)
{
if (self->klass->clear_key)
self->klass->clear_key (self, key_memory);
}
#endif /* __GTK_SORT_KEYS_PRIVATE_H__ */

View File

@ -133,6 +133,7 @@ gtk_private_sources = files([
'gtksearchengine.c',
'gtksearchenginemodel.c',
'gtksizerequestcache.c',
'gtksortkeys.c',
'gtkstyleanimation.c',
'gtkstylecascade.c',
'gtkstyleproperty.c',