Compare commits

..

10 Commits

Author SHA1 Message Date
Matthias Clasen
8815e1bb0c Add another incremental sort model
This one uses a very simple iterative mergesort.
2020-07-08 01:59:52 -04:00
Benjamin Otte
a57e0a4bf6 Add a 4th sortmodel implementation
This one tracks the original positions on top of just the items so that
it can remove items.

It now takes twice as much memory but removes half of a million items in
50ms.
2020-07-08 04:46:55 +02:00
Matthias Clasen
b74b0edf0d wip: incremental sort 2020-07-08 04:32:42 +02:00
Benjamin Otte
36fac37cda testsuite: Add some sorting performance tests 2020-07-08 04:12:33 +02:00
Benjamin Otte
91e2661306 stringlist: Convert to array
Stringlists are usually built and then never modified, and accessing
items through an array is faster.
2020-07-08 04:12:33 +02:00
Benjamin Otte
559ea908de gtk: Add a 2nd sortmodel implementation
This is the dumbest possible sortmodel using an array:
Just grab all the items, put them in the array, qsort() the array.
2020-07-08 04:12:33 +02:00
Benjamin Otte
f0b146d7c8 demo: Add faster sorters
This is just the existing sorters, but without the overhead of GObject
properties.
2020-07-08 00:44:40 +02:00
Benjamin Otte
a030197974 sorter: Remove a return_if_fail()
It's too expsensive.
2020-07-08 00:44:40 +02:00
Benjamin Otte
bf2bf948ab xxx: vector_set_size 2020-07-08 00:44:40 +02:00
Benjamin Otte
5cb49363fa sortlistmodel: Remove forgotten G_PARAM_CONSTRUCT_ONLY 2020-07-08 00:44:40 +02:00
12 changed files with 1558 additions and 64 deletions

View File

@@ -676,7 +676,7 @@ create_color_grid (void)
gtk_grid_view_set_max_columns (GTK_GRID_VIEW (gridview), 24);
gtk_grid_view_set_enable_rubberband (GTK_GRID_VIEW (gridview), TRUE);
model = G_LIST_MODEL (gtk_sor3_list_model_new (gtk_color_list_new (0), NULL));
model = G_LIST_MODEL (gtk_sor5_list_model_new (gtk_color_list_new (0), NULL));
selection = G_LIST_MODEL (gtk_multi_selection_new (model));
gtk_grid_view_set_model (GTK_GRID_VIEW (gridview), selection);
@@ -1012,7 +1012,7 @@ do_listview_colors (GtkWidget *do_widget)
button = gtk_button_new_with_mnemonic ("_Refill");
g_signal_connect (button, "clicked",
G_CALLBACK (refill),
gtk_sor3_list_model_get_model (GTK_SOR3_LIST_MODEL (model)));
gtk_sor5_list_model_get_model (GTK_SOR5_LIST_MODEL (model)));
gtk_header_bar_pack_start (GTK_HEADER_BAR (header), button);
@@ -1035,7 +1035,7 @@ do_listview_colors (GtkWidget *do_widget)
gtk_drop_down_set_from_strings (GTK_DROP_DOWN (dropdown), (const char *[]) { "8", "64", "512", "4096", "32768", "262144", "2097152", "16777216", NULL });
g_signal_connect (dropdown, "notify::selected",
G_CALLBACK (limit_changed_cb),
gtk_sor3_list_model_get_model (GTK_SOR3_LIST_MODEL (model)));
gtk_sor5_list_model_get_model (GTK_SOR5_LIST_MODEL (model)));
g_signal_connect (dropdown, "notify::selected",
G_CALLBACK (limit_changed_cb2),
label);

View File

@@ -235,6 +235,8 @@
#include <gtk/gtksorter.h>
#include <gtk/gtksor2listmodel.h>
#include <gtk/gtksor3listmodel.h>
#include <gtk/gtksor4listmodel.h>
#include <gtk/gtksor5listmodel.h>
#include <gtk/gtksortlistmodel.h>
#include <gtk/gtkstacksidebar.h>
#include <gtk/gtksizegroup.h>

View File

@@ -307,7 +307,7 @@ gtk_sor2_list_model_class_init (GtkSor2ListModelClass *class)
P_("Model"),
P_("The model being sorted"),
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
@@ -422,6 +422,13 @@ gtk_sor2_list_model_set_sorter (GtkSor2ListModel *self,
g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sor2_list_model_sorter_changed_cb), self);
gtk_sor2_list_model_sorter_changed_cb (sorter, GTK_SORTER_CHANGE_DIFFERENT, self);
}
else
{
guint n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
if (n_items > 1)
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
}

551
gtk/gtksor4listmodel.c Normal file
View File

@@ -0,0 +1,551 @@
/*
* Copyright © 2018 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtksor4listmodel.h"
#include "gtkintl.h"
#include "gtkprivate.h"
typedef struct _SortItem SortItem;
struct _SortItem
{
GObject *item;
guint position;
};
static void
sort_item_clear (gpointer data)
{
SortItem *si = data;
g_clear_object (&si->item);
}
#define GTK_VECTOR_ELEMENT_TYPE SortItem
#define GTK_VECTOR_TYPE_NAME SortArray
#define GTK_VECTOR_NAME sort_array
#define GTK_VECTOR_FREE_FUNC sort_item_clear
#define GTK_VECTOR_BY_VALUE 1
#include "gtkvectorimpl.c"
/**
* SECTION:gtksor4listmodel
* @title: GtkSor4ListModel
* @short_description: A list model that sorts its items
* @see_also: #GListModel, #GtkSorter
*
* #GtkSor4ListModel is a list model that takes a list model and
* sorts its elements according to a #GtkSorter.
*
* #GtkSor4ListModel is a generic model and because of that it
* cannot take advantage of any external knowledge when sorting.
* If you run into performance issues with #GtkSor4ListModel, it
* is strongly recommended that you write your own sorting list
* model.
*/
enum {
PROP_0,
PROP_MODEL,
PROP_SORTER,
NUM_PROPERTIES
};
struct _GtkSor4ListModel
{
GObject parent_instance;
GListModel *model;
GtkSorter *sorter;
SortArray items; /* empty if known unsorted */
};
struct _GtkSor4ListModelClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static GType
gtk_sor4_list_model_get_item_type (GListModel *list)
{
return G_TYPE_OBJECT;
}
static guint
gtk_sor4_list_model_get_n_items (GListModel *list)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (list);
if (self->model == NULL)
return 0;
return g_list_model_get_n_items (self->model);
}
static gpointer
gtk_sor4_list_model_get_item (GListModel *list,
guint position)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (list);
if (self->model == NULL)
return NULL;
if (sort_array_is_empty (&self->items))
return g_list_model_get_item (self->model, position);
if (position >= sort_array_get_size (&self->items))
return NULL;
return g_object_ref (sort_array_get (&self->items, position)->item);
}
static void
gtk_sor4_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_sor4_list_model_get_item_type;
iface->get_n_items = gtk_sor4_list_model_get_n_items;
iface->get_item = gtk_sor4_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkSor4ListModel, gtk_sor4_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sor4_list_model_model_init))
static void
gtk_sor4_list_model_clear_items (GtkSor4ListModel *self)
{
sort_array_clear (&self->items);
}
static gboolean
gtk_sor4_list_model_should_sort (GtkSor4ListModel *self)
{
return self->sorter != NULL &&
self->model != NULL &&
gtk_sorter_get_order (self->sorter) != GTK_SORTER_ORDER_NONE;
}
static void
gtk_sor4_list_model_create_items (GtkSor4ListModel *self)
{
guint i, n_items;
if (!gtk_sor4_list_model_should_sort (self))
return;
n_items = g_list_model_get_n_items (self->model);
sort_array_reserve (&self->items, n_items);
for (i = 0; i < n_items; i++)
{
sort_array_append (&self->items, &(SortItem) { g_list_model_get_item (self->model, i), i });
}
}
static int
sort_func (gconstpointer a,
gconstpointer b,
gpointer data)
{
SortItem *sa = (SortItem *) a;
SortItem *sb = (SortItem *) b;
return gtk_sorter_compare (data, sa->item, sb->item);
}
static void
gtk_sor4_list_model_resort (GtkSor4ListModel *self)
{
g_qsort_with_data (sort_array_get_data (&self->items),
sort_array_get_size (&self->items),
sizeof (SortItem),
sort_func,
self->sorter);
}
static void
gtk_sor4_list_model_remove_items (GtkSor4ListModel *self,
guint position,
guint removed,
guint added,
guint *unmodified_start,
guint *unmodified_end)
{
guint i, n_items, valid;
guint start, end;
n_items = sort_array_get_size (&self->items);
start = n_items;
end = n_items;
valid = 0;
for (i = 0; i < n_items; i++)
{
SortItem *si = sort_array_index (&self->items, i);
if (si->position >= position + removed)
si->position = si->position - removed + added;
else if (si->position >= position)
{
start = MIN (start, valid);
end = n_items - i - 1;
sort_item_clear (si);
continue;
}
if (valid < i)
*sort_array_index (&self->items, valid) = *sort_array_index (&self->items, i);
valid++;
}
g_assert (valid == n_items - removed);
memset (sort_array_index (&self->items, valid), 0, sizeof (SortItem) * removed);
sort_array_set_size (&self->items, valid);
*unmodified_start = start;
*unmodified_end = end;
}
static void
gtk_sor4_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkSor4ListModel *self)
{
guint i, n_items, start, end;
if (removed == 0 && added == 0)
return;
if (!gtk_sor4_list_model_should_sort (self))
{
g_list_model_items_changed (G_LIST_MODEL (self), position, removed, added);
return;
}
gtk_sor4_list_model_remove_items (self, position, removed, added, &start, &end);
if (added > 0)
{
sort_array_reserve (&self->items, sort_array_get_size (&self->items) + added);
for (i = position; i < position + added; i++)
{
sort_array_append (&self->items, &(SortItem) { g_list_model_get_item (self->model, i), i });
}
gtk_sor4_list_model_resort (self);
for (i = 0; i < start; i++)
{
SortItem *si = sort_array_get (&self->items, i);
if (si->position >= position && si->position < position + added)
{
start = i;
break;
}
}
n_items = sort_array_get_size (&self->items);
for (i = 0; i < end; i++)
{
SortItem *si = sort_array_get (&self->items, n_items - i - 1);
if (si->position >= position && si->position < position + added)
{
end = i;
break;
}
}
}
n_items = sort_array_get_size (&self->items) - start - end;
g_list_model_items_changed (G_LIST_MODEL (self), start, n_items - added + removed, n_items);
}
static void
gtk_sor4_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (object);
switch (prop_id)
{
case PROP_MODEL:
gtk_sor4_list_model_set_model (self, g_value_get_object (value));
break;
case PROP_SORTER:
gtk_sor4_list_model_set_sorter (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_sor4_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (object);
switch (prop_id)
{
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
case PROP_SORTER:
g_value_set_object (value, self->sorter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_sor4_list_model_sorter_changed_cb (GtkSorter *sorter,
int change,
GtkSor4ListModel *self)
{
guint n_items;
if (gtk_sorter_get_order (sorter) == GTK_SORTER_ORDER_NONE)
gtk_sor4_list_model_clear_items (self);
else if (sort_array_is_empty (&self->items))
gtk_sor4_list_model_create_items (self);
gtk_sor4_list_model_resort (self);
n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
if (n_items > 1)
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
}
static void
gtk_sor4_list_model_clear_model (GtkSor4ListModel *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model, gtk_sor4_list_model_items_changed_cb, self);
g_clear_object (&self->model);
gtk_sor4_list_model_clear_items (self);
}
static void
gtk_sor4_list_model_clear_sorter (GtkSor4ListModel *self)
{
if (self->sorter == NULL)
return;
g_signal_handlers_disconnect_by_func (self->sorter, gtk_sor4_list_model_sorter_changed_cb, self);
g_clear_object (&self->sorter);
gtk_sor4_list_model_clear_items (self);
}
static void
gtk_sor4_list_model_dispose (GObject *object)
{
GtkSor4ListModel *self = GTK_SOR4_LIST_MODEL (object);
gtk_sor4_list_model_clear_model (self);
gtk_sor4_list_model_clear_sorter (self);
G_OBJECT_CLASS (gtk_sor4_list_model_parent_class)->dispose (object);
};
static void
gtk_sor4_list_model_class_init (GtkSor4ListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_sor4_list_model_set_property;
gobject_class->get_property = gtk_sor4_list_model_get_property;
gobject_class->dispose = gtk_sor4_list_model_dispose;
/**
* GtkSor4ListModel:sorter:
*
* The sorter for this model
*/
properties[PROP_SORTER] =
g_param_spec_object ("sorter",
P_("Sorter"),
P_("The sorter for this model"),
GTK_TYPE_SORTER,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkSor4ListModel:model:
*
* The model being sorted
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("The model being sorted"),
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_sor4_list_model_init (GtkSor4ListModel *self)
{
}
/**
* gtk_sor4_list_model_new:
* @model: (allow-none): the model to sort
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Creates a new sort list model that uses the @sorter to sort @model.
*
* Returns: a new #GtkSor4ListModel
**/
GtkSor4ListModel *
gtk_sor4_list_model_new (GListModel *model,
GtkSorter *sorter)
{
GtkSor4ListModel *result;
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
g_return_val_if_fail (sorter == NULL || GTK_IS_SORTER (sorter), NULL);
result = g_object_new (GTK_TYPE_SOR4_LIST_MODEL,
"model", model,
"sorter", sorter,
NULL);
return result;
}
/**
* gtk_sor4_list_model_set_model:
* @self: a #GtkSor4ListModel
* @model: (allow-none): The model to be sorted
*
* Sets the model to be sorted. The @model's item type must conform to
* the item type of @self.
**/
void
gtk_sor4_list_model_set_model (GtkSor4ListModel *self,
GListModel *model)
{
guint removed, added;
g_return_if_fail (GTK_IS_SOR4_LIST_MODEL (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (self->model == model)
return;
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
gtk_sor4_list_model_clear_model (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_sor4_list_model_items_changed_cb), self);
added = g_list_model_get_n_items (model);
gtk_sor4_list_model_create_items (self);
gtk_sor4_list_model_resort (self);
}
else
added = 0;
if (removed > 0 || added > 0)
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_sor4_list_model_get_model:
* @self: a #GtkSor4ListModel
*
* Gets the model currently sorted or %NULL if none.
*
* Returns: (nullable) (transfer none): The model that gets sorted
**/
GListModel *
gtk_sor4_list_model_get_model (GtkSor4ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR4_LIST_MODEL (self), NULL);
return self->model;
}
/**
* gtk_sor4_list_model_set_sorter:
* @self: a #GtkSor4ListModel
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Sets a new sorter on @self.
*/
void
gtk_sor4_list_model_set_sorter (GtkSor4ListModel *self,
GtkSorter *sorter)
{
g_return_if_fail (GTK_IS_SOR4_LIST_MODEL (self));
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
gtk_sor4_list_model_clear_sorter (self);
if (sorter)
{
self->sorter = g_object_ref (sorter);
g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sor4_list_model_sorter_changed_cb), self);
gtk_sor4_list_model_sorter_changed_cb (sorter, GTK_SORTER_CHANGE_DIFFERENT, self);
}
else
{
guint n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
if (n_items > 1)
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
}
/**
* gtk_sor4_list_model_get_sorter:
* @self: a #GtkSor4LisTModel
*
* Gets the sorter that is used to sort @self.
*
* Returns: (nullable) (transfer none): the sorter of #self
*/
GtkSorter *
gtk_sor4_list_model_get_sorter (GtkSor4ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR4_LIST_MODEL (self), NULL);
return self->sorter;
}

57
gtk/gtksor4listmodel.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* Copyright © 2018 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_SOR4_LIST_MODEL_H__
#define __GTK_SOR4_LIST_MODEL_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gio/gio.h>
#include <gtk/gtkwidget.h>
#include <gtk/gtksorter.h>
G_BEGIN_DECLS
#define GTK_TYPE_SOR4_LIST_MODEL (gtk_sor4_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkSor4ListModel, gtk_sor4_list_model, GTK, SOR4_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkSor4ListModel * gtk_sor4_list_model_new (GListModel *model,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
void gtk_sor4_list_model_set_sorter (GtkSor4ListModel *self,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_sor4_list_model_get_sorter (GtkSor4ListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_sor4_list_model_set_model (GtkSor4ListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_sor4_list_model_get_model (GtkSor4ListModel *self);
G_END_DECLS
#endif /* __GTK_SOR4_LIST_MODEL_H__ */

546
gtk/gtksor5listmodel.c Normal file
View File

@@ -0,0 +1,546 @@
/*
* Copyright © 2018 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#include "config.h"
#include "gtksor5listmodel.h"
#include "gtkintl.h"
#include "gtkprivate.h"
#define GTK_VECTOR_ELEMENT_TYPE GObject *
#define GTK_VECTOR_TYPE_NAME SortArray
#define GTK_VECTOR_NAME sort_array
#define GTK_VECTOR_FREE_FUNC g_object_unref
#define GTK_VECTOR_PREALLOC 16
#include "gtkvectorimpl.c"
/**
* SECTION:gtksor5listmodel
* @title: GtkSor5ListModel
* @short_description: A list model that sorts its items
* @see_also: #GListModel, #GtkSorter
*
* #GtkSor5ListModel is a list model that takes a list model and
* sorts its elements according to a #GtkSorter.
*
* #GtkSor5ListModel is a generic model and because of that it
* cannot take advantage of any external knowledge when sorting.
* If you run into performance issues with #GtkSor5ListModel, it
* is strongly recommended that you write your own sorting list
* model.
*/
enum {
PROP_0,
PROP_MODEL,
PROP_SORTER,
NUM_PROPERTIES
};
struct _GtkSor5ListModel
{
GObject parent_instance;
GListModel *model;
GtkSorter *sorter;
SortArray items; /* empty if known unsorted */
guint sorting_cb;
guint size;
guint start;
};
struct _GtkSor5ListModelClass
{
GObjectClass parent_class;
};
static GParamSpec *properties[NUM_PROPERTIES] = { NULL, };
static GType
gtk_sor5_list_model_get_item_type (GListModel *list)
{
return G_TYPE_OBJECT;
}
static guint
gtk_sor5_list_model_get_n_items (GListModel *list)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (list);
if (self->model == NULL)
return 0;
return g_list_model_get_n_items (self->model);
}
static gpointer
gtk_sor5_list_model_get_item (GListModel *list,
guint position)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (list);
if (self->model == NULL)
return NULL;
if (sort_array_is_empty (&self->items))
return g_list_model_get_item (self->model, position);
if (position >= sort_array_get_size (&self->items))
return NULL;
return g_object_ref (sort_array_get (&self->items, position));
}
static void
gtk_sor5_list_model_model_init (GListModelInterface *iface)
{
iface->get_item_type = gtk_sor5_list_model_get_item_type;
iface->get_n_items = gtk_sor5_list_model_get_n_items;
iface->get_item = gtk_sor5_list_model_get_item;
}
G_DEFINE_TYPE_WITH_CODE (GtkSor5ListModel, gtk_sor5_list_model, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (G_TYPE_LIST_MODEL, gtk_sor5_list_model_model_init))
static void
gtk_sor5_list_model_clear_items (GtkSor5ListModel *self)
{
sort_array_clear (&self->items);
}
static void
gtk_sor5_list_model_create_items (GtkSor5ListModel *self)
{
guint i, n_items;
if (self->sorter == NULL ||
self->model == NULL ||
gtk_sorter_get_order (self->sorter) == GTK_SORTER_ORDER_NONE)
return;
n_items = g_list_model_get_n_items (self->model);
sort_array_reserve (&self->items, n_items);
for (i = 0; i < n_items; i++)
sort_array_append (&self->items, g_list_model_get_item (self->model, i));
}
static void
gtk_sor5_list_model_stop_sorting (GtkSor5ListModel *self)
{
g_clear_handle_id (&self->sorting_cb, g_source_remove);
}
static guint
merge (SortArray *items,
guint start,
guint mid,
guint end,
GtkSorter *sorter)
{
int start2 = mid + 1;
int c = 1;
if (gtk_sorter_compare (sorter,
sort_array_get (items, mid),
sort_array_get (items, start2)) <= 0)
return 1;
while (start <= mid && start2 <= end)
{
c++;
if (gtk_sorter_compare (sorter,
sort_array_get (items, start),
sort_array_get (items, start2)) <= 0)
start++;
else
{
GObject *value = sort_array_get (items, start2);
int index = start2;
while (index != start)
{
c++;
*sort_array_index (items, index) = sort_array_get (items, index - 1);
index--;
}
c++;
*sort_array_index (items, start) = value;
start++;
mid++;
start2++;
}
}
return c;
}
static gboolean
gtk_sor5_list_model_sort_cb (gpointer data)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (data);
guint n_items = sort_array_get_size (&self->items);
guint i;
guint s, e;
s = self->start;
e = self->start;
i = 0;
while (i < 10000)
{
guint mid = MIN (self->start + self->size - 1, n_items - 1);
guint end = MIN (self->start + 2 * self->size - 1, n_items - 1);
i += merge (&self->items, self->start, mid, end, self->sorter);
s = MIN (s, self->start);
e = MAX (e, end);
self->start += 2 * self->size;
if (self->start >= n_items - 1)
{
self->start = 0;
self->size *= 2;
if (self->size >= n_items)
{
gtk_sor5_list_model_stop_sorting (self);
break;
}
}
}
//g_print ("items changed %u:%u\n", s, e - s + 1);
g_list_model_items_changed (G_LIST_MODEL (self), s, e - s + 1, e - s + 1);
return G_SOURCE_CONTINUE;
}
static void
gtk_sor5_list_model_start_sorting (GtkSor5ListModel *self)
{
if (sort_array_get_size (&self->items) == 0)
return;
g_assert (self->sorting_cb == 0);
self->size = 1;
self->start = 0;
self->sorting_cb = g_idle_add (gtk_sor5_list_model_sort_cb, self);
g_source_set_name_by_id (self->sorting_cb, "[gtk] gtk_sor5_list_model_sort_cb");
}
static void
gtk_sor5_list_model_resort (GtkSor5ListModel *self)
{
gtk_sor5_list_model_stop_sorting (self);
gtk_sor5_list_model_start_sorting (self);
}
static void
gtk_sor5_list_model_items_changed_cb (GListModel *model,
guint position,
guint removed,
guint added,
GtkSor5ListModel *self)
{
guint n_items;
/* doesn't free() the array */
sort_array_set_size (&self->items, 0);
gtk_sor5_list_model_create_items (self);
gtk_sor5_list_model_resort (self);
n_items = g_list_model_get_n_items (model);
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items - added + removed, n_items);
}
static void
gtk_sor5_list_model_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (object);
switch (prop_id)
{
case PROP_MODEL:
gtk_sor5_list_model_set_model (self, g_value_get_object (value));
break;
case PROP_SORTER:
gtk_sor5_list_model_set_sorter (self, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_sor5_list_model_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (object);
switch (prop_id)
{
case PROP_MODEL:
g_value_set_object (value, self->model);
break;
case PROP_SORTER:
g_value_set_object (value, self->sorter);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gtk_sor5_list_model_sorter_changed_cb (GtkSorter *sorter,
int change,
GtkSor5ListModel *self)
{
guint n_items;
if (gtk_sorter_get_order (sorter) == GTK_SORTER_ORDER_NONE)
gtk_sor5_list_model_clear_items (self);
else if (sort_array_is_empty (&self->items))
gtk_sor5_list_model_create_items (self);
gtk_sor5_list_model_resort (self);
n_items = g_list_model_get_n_items (G_LIST_MODEL (self));
if (n_items > 1)
g_list_model_items_changed (G_LIST_MODEL (self), 0, n_items, n_items);
}
static void
gtk_sor5_list_model_clear_model (GtkSor5ListModel *self)
{
if (self->model == NULL)
return;
g_signal_handlers_disconnect_by_func (self->model, gtk_sor5_list_model_items_changed_cb, self);
g_clear_object (&self->model);
gtk_sor5_list_model_stop_sorting (self);
gtk_sor5_list_model_clear_items (self);
}
static void
gtk_sor5_list_model_clear_sorter (GtkSor5ListModel *self)
{
if (self->sorter == NULL)
return;
g_signal_handlers_disconnect_by_func (self->sorter, gtk_sor5_list_model_sorter_changed_cb, self);
g_clear_object (&self->sorter);
gtk_sor5_list_model_stop_sorting (self);
gtk_sor5_list_model_clear_items (self);
}
static void
gtk_sor5_list_model_dispose (GObject *object)
{
GtkSor5ListModel *self = GTK_SOR5_LIST_MODEL (object);
gtk_sor5_list_model_stop_sorting (self);
gtk_sor5_list_model_clear_model (self);
gtk_sor5_list_model_clear_sorter (self);
G_OBJECT_CLASS (gtk_sor5_list_model_parent_class)->dispose (object);
};
static void
gtk_sor5_list_model_class_init (GtkSor5ListModelClass *class)
{
GObjectClass *gobject_class = G_OBJECT_CLASS (class);
gobject_class->set_property = gtk_sor5_list_model_set_property;
gobject_class->get_property = gtk_sor5_list_model_get_property;
gobject_class->dispose = gtk_sor5_list_model_dispose;
/**
* GtkSor5ListModel:sorter:
*
* The sorter for this model
*/
properties[PROP_SORTER] =
g_param_spec_object ("sorter",
P_("Sorter"),
P_("The sorter for this model"),
GTK_TYPE_SORTER,
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
/**
* GtkSor5ListModel:model:
*
* The model being sorted
*/
properties[PROP_MODEL] =
g_param_spec_object ("model",
P_("Model"),
P_("The model being sorted"),
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}
static void
gtk_sor5_list_model_init (GtkSor5ListModel *self)
{
}
/**
* gtk_sor5_list_model_new:
* @model: (allow-none): the model to sort
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Creates a new sort list model that uses the @sorter to sort @model.
*
* Returns: a new #GtkSor5ListModel
**/
GtkSor5ListModel *
gtk_sor5_list_model_new (GListModel *model,
GtkSorter *sorter)
{
GtkSor5ListModel *result;
g_return_val_if_fail (model == NULL || G_IS_LIST_MODEL (model), NULL);
g_return_val_if_fail (sorter == NULL || GTK_IS_SORTER (sorter), NULL);
result = g_object_new (GTK_TYPE_SOR5_LIST_MODEL,
"model", model,
"sorter", sorter,
NULL);
return result;
}
/**
* gtk_sor5_list_model_set_model:
* @self: a #GtkSor5ListModel
* @model: (allow-none): The model to be sorted
*
* Sets the model to be sorted. The @model's item type must conform to
* the item type of @self.
**/
void
gtk_sor5_list_model_set_model (GtkSor5ListModel *self,
GListModel *model)
{
guint removed, added;
g_return_if_fail (GTK_IS_SOR5_LIST_MODEL (self));
g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model));
if (self->model == model)
return;
removed = g_list_model_get_n_items (G_LIST_MODEL (self));
gtk_sor5_list_model_clear_model (self);
if (model)
{
self->model = g_object_ref (model);
g_signal_connect (model, "items-changed", G_CALLBACK (gtk_sor5_list_model_items_changed_cb), self);
added = g_list_model_get_n_items (model);
gtk_sor5_list_model_create_items (self);
gtk_sor5_list_model_resort (self);
}
else
added = 0;
if (removed > 0 || added > 0)
g_list_model_items_changed (G_LIST_MODEL (self), 0, removed, added);
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_MODEL]);
}
/**
* gtk_sor5_list_model_get_model:
* @self: a #GtkSor5ListModel
*
* Gets the model currently sorted or %NULL if none.
*
* Returns: (nullable) (transfer none): The model that gets sorted
**/
GListModel *
gtk_sor5_list_model_get_model (GtkSor5ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR5_LIST_MODEL (self), NULL);
return self->model;
}
/**
* gtk_sor5_list_model_set_sorter:
* @self: a #GtkSor5ListModel
* @sorter: (allow-none): the #GtkSorter to sort @model with
*
* Sets a new sorter on @self.
*/
void
gtk_sor5_list_model_set_sorter (GtkSor5ListModel *self,
GtkSorter *sorter)
{
g_return_if_fail (GTK_IS_SOR5_LIST_MODEL (self));
g_return_if_fail (sorter == NULL || GTK_IS_SORTER (sorter));
gtk_sor5_list_model_clear_sorter (self);
if (sorter)
{
self->sorter = g_object_ref (sorter);
g_signal_connect (sorter, "changed", G_CALLBACK (gtk_sor5_list_model_sorter_changed_cb), self);
gtk_sor5_list_model_sorter_changed_cb (sorter, GTK_SORTER_CHANGE_DIFFERENT, self);
}
g_object_notify_by_pspec (G_OBJECT (self), properties[PROP_SORTER]);
}
/**
* gtk_sor5_list_model_get_sorter:
* @self: a #GtkSor5LisTModel
*
* Gets the sorter that is used to sort @self.
*
* Returns: (nullable) (transfer none): the sorter of #self
*/
GtkSorter *
gtk_sor5_list_model_get_sorter (GtkSor5ListModel *self)
{
g_return_val_if_fail (GTK_IS_SOR5_LIST_MODEL (self), NULL);
return self->sorter;
}

57
gtk/gtksor5listmodel.h Normal file
View File

@@ -0,0 +1,57 @@
/*
* Copyright © 2018 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/>.
*
* Authors: Benjamin Otte <otte@gnome.org>
*/
#ifndef __GTK_SOR5_LIST_MODEL_H__
#define __GTK_SOR5_LIST_MODEL_H__
#if !defined (__GTK_H_INSIDE__) && !defined (GTK_COMPILATION)
#error "Only <gtk/gtk.h> can be included directly."
#endif
#include <gio/gio.h>
#include <gtk/gtkwidget.h>
#include <gtk/gtksorter.h>
G_BEGIN_DECLS
#define GTK_TYPE_SOR5_LIST_MODEL (gtk_sor5_list_model_get_type ())
GDK_AVAILABLE_IN_ALL
G_DECLARE_FINAL_TYPE (GtkSor5ListModel, gtk_sor5_list_model, GTK, SOR5_LIST_MODEL, GObject)
GDK_AVAILABLE_IN_ALL
GtkSor5ListModel * gtk_sor5_list_model_new (GListModel *model,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
void gtk_sor5_list_model_set_sorter (GtkSor5ListModel *self,
GtkSorter *sorter);
GDK_AVAILABLE_IN_ALL
GtkSorter * gtk_sor5_list_model_get_sorter (GtkSor5ListModel *self);
GDK_AVAILABLE_IN_ALL
void gtk_sor5_list_model_set_model (GtkSor5ListModel *self,
GListModel *model);
GDK_AVAILABLE_IN_ALL
GListModel * gtk_sor5_list_model_get_model (GtkSor5ListModel *self);
G_END_DECLS
#endif /* __GTK_SOR5_LIST_MODEL_H__ */

View File

@@ -403,7 +403,7 @@ gtk_sort_list_model_class_init (GtkSortListModelClass *class)
P_("Model"),
P_("The model being sorted"),
G_TYPE_LIST_MODEL,
GTK_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_EXPLICIT_NOTIFY);
GTK_PARAM_READWRITE | G_PARAM_EXPLICIT_NOTIFY);
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
}

View File

@@ -57,6 +57,12 @@
*/
#define GTK_VECTOR_ELEMENT_TYPE GtkStringObject *
#define GTK_VECTOR_NAME objects
#define GTK_VECTOR_TYPE_NAME Objects
#define GTK_VECTOR_FREE_FUNC g_object_unref
#include "gtkvectorimpl.c"
struct _GtkStringObject
{
GObject parent_instance;
@@ -190,7 +196,7 @@ struct _GtkStringList
{
GObject parent_instance;
GSequence *items;
Objects items;
};
struct _GtkStringListClass
@@ -209,7 +215,7 @@ gtk_string_list_get_n_items (GListModel *list)
{
GtkStringList *self = GTK_STRING_LIST (list);
return g_sequence_get_length (self->items);
return objects_get_size (&self->items);
}
static gpointer
@@ -217,14 +223,11 @@ gtk_string_list_get_item (GListModel *list,
guint position)
{
GtkStringList *self = GTK_STRING_LIST (list);
GSequenceIter *iter;
iter = g_sequence_get_iter_at_pos (self->items, position);
if (g_sequence_iter_is_end (iter))
if (position >= objects_get_size (&self->items))
return NULL;
else
return g_object_ref (g_sequence_get (iter));
return g_object_ref (objects_get (&self->items, position));
}
static void
@@ -331,7 +334,7 @@ item_end_element (GtkBuildableParseContext *context,
g_string_assign (data->string, translated);
}
g_sequence_append (data->list->items, gtk_string_object_new (data->string->str));
gtk_string_list_append (data->list, data->string->str);
}
data->translatable = FALSE;
@@ -411,7 +414,7 @@ gtk_string_list_dispose (GObject *object)
{
GtkStringList *self = GTK_STRING_LIST (object);
g_clear_pointer (&self->items, g_sequence_free);
objects_clear (&self->items);
G_OBJECT_CLASS (gtk_string_list_parent_class)->dispose (object);
}
@@ -427,7 +430,7 @@ gtk_string_list_class_init (GtkStringListClass *class)
static void
gtk_string_list_init (GtkStringList *self)
{
self->items = g_sequence_new (g_object_unref);
objects_init (&self->items);
}
/**
@@ -476,32 +479,19 @@ gtk_string_list_splice (GtkStringList *self,
guint n_removals,
const char * const *additions)
{
GSequenceIter *it;
guint add, n_items;
guint add;
g_return_if_fail (GTK_IS_STRING_LIST (self));
g_return_if_fail (position + n_removals >= position); /* overflow */
g_return_if_fail (position + n_removals <= objects_get_size (&self->items));
n_items = g_sequence_get_length (self->items);
g_return_if_fail (position + n_removals <= n_items);
it = g_sequence_get_iter_at_pos (self->items, position);
if (n_removals)
{
GSequenceIter *end;
end = g_sequence_iter_move (it, n_removals);
g_sequence_remove_range (it, end);
it = end;
}
objects_splice (&self->items, position, n_removals, NULL, 0);
if (additions)
{
for (add = 0; additions[add]; add++)
{
g_sequence_insert_before (it, gtk_string_object_new (additions[add]));
objects_append (&self->items, gtk_string_object_new (additions[add]));
}
}
else
@@ -525,14 +515,11 @@ void
gtk_string_list_append (GtkStringList *self,
const char *string)
{
guint n_items;
g_return_if_fail (GTK_IS_STRING_LIST (self));
n_items = g_sequence_get_length (self->items);
g_sequence_append (self->items, gtk_string_object_new (string));
objects_append (&self->items, gtk_string_object_new (string));
g_list_model_items_changed (G_LIST_MODEL (self), n_items, 0, 1);
g_list_model_items_changed (G_LIST_MODEL (self), objects_get_size (&self->items) - 1, 0, 1);
}
/**
@@ -554,14 +541,11 @@ void
gtk_string_list_take (GtkStringList *self,
char *string)
{
guint n_items;
g_return_if_fail (GTK_IS_STRING_LIST (self));
n_items = g_sequence_get_length (self->items);
g_sequence_append (self->items, gtk_string_object_new_take (string));
objects_append (&self->items, gtk_string_object_new_take (string));
g_list_model_items_changed (G_LIST_MODEL (self), n_items, 0, 1);
g_list_model_items_changed (G_LIST_MODEL (self), objects_get_size (&self->items) - 1, 0, 1);
}
/**
@@ -576,16 +560,9 @@ void
gtk_string_list_remove (GtkStringList *self,
guint position)
{
GSequenceIter *iter;
g_return_if_fail (GTK_IS_STRING_LIST (self));
iter = g_sequence_get_iter_at_pos (self->items, position);
g_return_if_fail (!g_sequence_iter_is_end (iter));
g_sequence_remove (iter);
g_list_model_items_changed (G_LIST_MODEL (self), position, 1, 0);
gtk_string_list_splice (self, position, 1, NULL);
}
/**
@@ -605,20 +582,10 @@ const char *
gtk_string_list_get_string (GtkStringList *self,
guint position)
{
GSequenceIter *iter;
g_return_val_if_fail (GTK_IS_STRING_LIST (self), NULL);
iter = g_sequence_get_iter_at_pos (self->items, position);
if (position >= objects_get_size (&self->items))
return NULL;
if (g_sequence_iter_is_end (iter))
{
return NULL;
}
else
{
GtkStringObject *obj = g_sequence_get (iter);
return obj->string;
}
return objects_get (&self->items, position)->string;
}

View File

@@ -373,6 +373,8 @@ gtk_public_sources = files([
'gtksorter.c',
'gtksor2listmodel.c',
'gtksor3listmodel.c',
'gtksor4listmodel.c',
'gtksor5listmodel.c',
'gtksortlistmodel.c',
'gtkspinbutton.c',
'gtkspinner.c',
@@ -644,6 +646,8 @@ gtk_public_headers = files([
'gtksorter.h',
'gtksor2listmodel.h',
'gtksor3listmodel.h',
'gtksor4listmodel.h',
'gtksor5listmodel.h',
'gtksortlistmodel.h',
'gtkspinbutton.h',
'gtkspinner.h',

View File

@@ -59,6 +59,7 @@ tests = [
['slicelistmodel'],
['sorter'],
['sortlistmodel'],
['sort-performance'],
['spinbutton'],
['stringlist'],
['templates'],

View File

@@ -0,0 +1,302 @@
#include <gtk/gtk.h>
#include <math.h>
#define MAX_SIZE 1024000
#define MAX_TIME (G_USEC_PER_SEC / 2)
static inline guint
quick_random (guint prev)
{
prev ^= prev << 13;
prev ^= prev >> 17;
prev ^= prev << 5;
return prev;
}
static guint comparisons = 0;
static int
compare_string_object (gconstpointer a,
gconstpointer b,
gpointer unused)
{
GtkStringObject *sa = (GtkStringObject *) a;
GtkStringObject *sb = (GtkStringObject *) b;
comparisons++;
return gtk_ordering_from_cmpfunc (strcmp (gtk_string_object_get_string (sa),
gtk_string_object_get_string (sb)));
}
static GtkSorter *
create_sorter (void)
{
return gtk_custom_sorter_new (compare_string_object, NULL, NULL);
}
G_GNUC_UNUSED static void
set_model (const char *testname,
GType type,
GListModel *source,
guint random)
{
gint64 start, end, total;
GtkSliceListModel *slice;
GtkSorter *sorter;
GListModel *sort;
guint size = 1000;
slice = gtk_slice_list_model_new (source, 0, size);
sorter = create_sorter ();
sort = g_object_new (type,
"sorter", sorter,
NULL);
g_object_unref (sorter);
while (TRUE)
{
comparisons = 0;
start = g_get_monotonic_time ();
g_object_set (sort, "model", slice, NULL);
end = g_get_monotonic_time ();
total = (end - start);
g_print ("\"%s\", \"%s\",%8u,%8uus,%8u\n",
testname,
g_type_name (type),
size,
(guint) total,
comparisons);
if (total > MAX_TIME)
break;
size *= 2;
if (4 * total > 2 * MAX_TIME)
break;
gtk_slice_list_model_set_size (slice, size);
g_object_set (sort, "model", NULL, NULL);
}
g_object_unref (sort);
g_object_unref (slice);
}
static void
append (const char *testname,
GType type,
GListModel *source,
guint random,
guint fraction)
{
gint64 start, end, total;
GtkSliceListModel *slice;
GtkSorter *sorter;
GListModel *sort;
guint size = 1000;
slice = gtk_slice_list_model_new (source, 0, (fraction - 1) * size / fraction);
sorter = create_sorter ();
sort = g_object_new (type,
"model", slice,
"sorter", sorter,
NULL);
g_object_unref (sorter);
while (TRUE)
{
gtk_slice_list_model_set_size (slice, (fraction - 1) * size / fraction);
comparisons = 0;
start = g_get_monotonic_time ();
gtk_slice_list_model_set_size (slice, size);
end = g_get_monotonic_time ();
total = (end - start);
g_print ("\"%s\", \"%s\",%8u,%8uus,%8u\n",
testname,
g_type_name (type),
size,
(guint) total,
comparisons);
if (total > MAX_TIME)
break;
size *= 2;
if (4 * total > 2 * MAX_TIME ||
size > MAX_SIZE)
break;
}
g_object_unref (sort);
g_object_unref (slice);
}
static void
append_half (const char *name,
GType type,
GListModel *source,
guint random)
{
append (name, type, source, random, 2);
}
static void
append_10th (const char *name,
GType type,
GListModel *source,
guint random)
{
append (name, type, source, random, 10);
}
static void
append_100th (const char *name,
GType type,
GListModel *source,
guint random)
{
append (name, type, source, random, 100);
}
static void
remove_test (const char *testname,
GType type,
GListModel *source,
guint random,
guint fraction)
{
gint64 start, end, total;
GtkSliceListModel *slice;
GtkSorter *sorter;
GListModel *sort;
guint size = 1000;
slice = gtk_slice_list_model_new (source, 0, size);
sorter = create_sorter ();
sort = g_object_new (type,
"model", slice,
"sorter", sorter,
NULL);
g_object_unref (sorter);
while (TRUE)
{
gtk_slice_list_model_set_size (slice, size);
comparisons = 0;
start = g_get_monotonic_time ();
gtk_slice_list_model_set_size (slice, (fraction - 1) * size / fraction);
end = g_get_monotonic_time ();
total = (end - start);
g_print ("\"%s\", \"%s\",%8u,%8uus,%8u\n",
testname,
g_type_name (type),
size,
(guint) total,
comparisons);
if (total > MAX_TIME)
break;
size *= 2;
if (4 * total > 2 * MAX_TIME ||
size > MAX_SIZE)
break;
}
g_object_unref (sort);
g_object_unref (slice);
}
static void
remove_half (const char *name,
GType type,
GListModel *source,
guint random)
{
remove_test (name, type, source, random, 2);
}
static void
remove_10th (const char *name,
GType type,
GListModel *source,
guint random)
{
remove_test (name, type, source, random, 10);
}
static void
remove_100th (const char *name,
GType type,
GListModel *source,
guint random)
{
remove_test (name, type, source, random, 100);
}
static void
run_test (GtkStringList *source,
const char * const *tests,
const char *test_name,
void (* test_func) (const char *name, GType type, GListModel *source, guint random))
{
GType types[] = {
GTK_TYPE_SORT_LIST_MODEL,
GTK_TYPE_SOR2_LIST_MODEL,
GTK_TYPE_SOR3_LIST_MODEL,
GTK_TYPE_SOR4_LIST_MODEL
};
guint random = g_random_int ();
guint i;
if (tests != NULL && !g_strv_contains (tests, test_name))
return;
for (i = 0; i < G_N_ELEMENTS (types); i++)
{
test_func (test_name, types[i], G_LIST_MODEL (source), random);
}
}
int
main (int argc, char *argv[])
{
GtkStringList *source;
guint random = g_random_int ();
guint i;
const char * const *tests;
gtk_test_init (&argc, &argv);
source = gtk_string_list_new (NULL);
for (i = 0; i < MAX_SIZE; i++)
{
gtk_string_list_take (source, g_strdup_printf ("%u", random));
random = quick_random (random);
}
if (argc < 2)
tests = NULL;
else
tests = (const char **) argv + 1;
g_print ("\"test\",\"model\",\"model size\",\"time\",\"comparisons\"\n");
run_test (source, tests, "set-model", set_model);
run_test (source, tests, "append-half", append_half);
run_test (source, tests, "append-10th", append_10th);
run_test (source, tests, "append-100th", append_100th);
run_test (source, tests, "remove-half", remove_half);
run_test (source, tests, "remove-10th", remove_10th);
run_test (source, tests, "remove-100th", remove_100th);
return g_test_run ();
}