summaryrefslogtreecommitdiff
path: root/gtk/gtkset.c
diff options
context:
space:
mode:
Diffstat (limited to 'gtk/gtkset.c')
-rw-r--r--gtk/gtkset.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/gtk/gtkset.c b/gtk/gtkset.c
new file mode 100644
index 0000000000..807743a7dc
--- /dev/null
+++ b/gtk/gtkset.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright © 2019 Red Hat, Inc.
+ *
+ * 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: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#include "gtkset.h"
+
+/* Store a set of unsigned integers as a sorted array of ranges.
+ */
+
+typedef struct
+{
+ guint first;
+ guint n_items;
+} Range;
+
+struct _GtkSet
+{
+ GArray *ranges;
+};
+
+typedef struct
+{
+ GtkSet *set;
+ Range *current;
+ int idx;
+ guint pos;
+} GtkRealSetIter;
+
+GtkSet *
+gtk_set_new (void)
+{
+ GtkSet *set;
+
+ set = g_new (GtkSet, 1);
+ set->ranges = g_array_new (FALSE, FALSE, sizeof (Range));
+
+ return set;
+}
+
+void
+gtk_set_free (GtkSet *set)
+{
+ g_array_free (set->ranges, TRUE);
+ g_free (set);
+}
+
+gboolean
+gtk_set_contains (GtkSet *set,
+ guint item)
+{
+ int i;
+
+ for (i = 0; i < set->ranges->len; i++)
+ {
+ Range *r = &g_array_index (set->ranges, Range, i);
+
+ if (item < r->first)
+ return FALSE;
+
+ if (item < r->first + r->n_items)
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void
+gtk_set_remove_all (GtkSet *set)
+{
+ g_array_set_size (set->ranges, 0);
+}
+
+static int
+range_compare (Range *r, Range *s)
+{
+ int ret = 0;
+
+ if (r->first + r->n_items < s->first)
+ ret = -1;
+ else if (s->first + s->n_items < r->first)
+ ret = 1;
+
+ return ret;
+}
+
+void
+gtk_set_add_range (GtkSet *set,
+ guint first_item,
+ guint n_items)
+{
+ int i;
+ Range s;
+ int first = -1;
+ int last = -1;
+
+ s.first = first_item;
+ s.n_items = n_items;
+
+ for (i = 0; i < set->ranges->len; i++)
+ {
+ Range *r = &g_array_index (set->ranges, Range, i);
+ int cmp = range_compare (&s, r);
+
+ if (cmp < 0)
+ break;
+
+ if (cmp == 0)
+ {
+ if (first < 0)
+ first = i;
+ last = i;
+ }
+ }
+
+ if (first > -1)
+ {
+ Range *r;
+ guint start;
+ guint end;
+
+ r = &g_array_index (set->ranges, Range, first);
+ start = MIN (s.first, r->first);
+
+ r = &g_array_index (set->ranges, Range, last);
+ end = MAX (s.first + s.n_items - 1, r->first + r->n_items - 1);
+
+ s.first = start;
+ s.n_items = end - start + 1;
+
+ g_array_remove_range (set->ranges, first, last - first + 1);
+ g_array_insert_val (set->ranges, first, s);
+ }
+ else
+ g_array_insert_val (set->ranges, i, s);
+}
+
+void
+gtk_set_remove_range (GtkSet *set,
+ guint first_item,
+ guint n_items)
+{
+ Range s;
+ int i;
+ int first = -1;
+ int last = -1;
+
+ s.first = first_item;
+ s.n_items = n_items;
+
+ for (i = 0; i < set->ranges->len; i++)
+ {
+ Range *r = &g_array_index (set->ranges, Range, i);
+ int cmp = range_compare (&s, r);
+
+ if (cmp < 0)
+ break;
+
+ if (cmp == 0)
+ {
+ if (first < 0)
+ first = i;
+ last = i;
+ }
+ }
+
+ if (first > -1)
+ {
+ Range *r;
+ Range a[2];
+ int k = 0;
+
+ r = &g_array_index (set->ranges, Range, first);
+ if (r->first < s.first)
+ {
+ a[k].first = r->first;
+ a[k].n_items = s.first - r->first;
+ k++;
+ }
+ r = &g_array_index (set->ranges, Range, last);
+ if (r->first + r->n_items > s.first + s.n_items)
+ {
+ a[k].first = s.first + s.n_items;
+ a[k].n_items = r->first + r->n_items - a[k].first;
+ k++;
+ }
+ g_array_remove_range (set->ranges, first, last - first + 1);
+ if (k > 0)
+ g_array_insert_vals (set->ranges, first, a, k);
+ }
+}
+
+void
+gtk_set_find_range (GtkSet *set,
+ guint position,
+ guint upper_bound,
+ guint *start,
+ guint *n_items,
+ gboolean *contained)
+{
+ int i;
+ int last = 0;
+
+ if (position >= upper_bound)
+ {
+ *start = 0;
+ *n_items = 0;
+ *contained = FALSE;
+ return;
+ }
+
+ for (i = 0; i < set->ranges->len; i++)
+ {
+ Range *r = &g_array_index (set->ranges, Range, i);
+
+ if (position < r->first)
+ {
+ *start = last;
+ *n_items = r->first - last;
+ *contained = FALSE;
+
+ return;
+ }
+ else if (r->first <= position && position < r->first + r->n_items)
+ {
+ *start = r->first;
+ *n_items = r->n_items;
+ *contained = TRUE;
+
+ return;
+ }
+ else
+ last = r->first + r->n_items;
+ }
+
+ *start = last;
+ *n_items = upper_bound - last;
+ *contained = FALSE;
+}
+
+void
+gtk_set_add_item (GtkSet *set,
+ guint item)
+{
+ gtk_set_add_range (set, item, 1);
+}
+
+void
+gtk_set_remove_item (GtkSet *set,
+ guint item)
+{
+ gtk_set_remove_range (set, item, 1);
+}
+
+/* This is peculiar operation: Replace every number n >= first by n + shift
+ * This is only supported for negatie if the shifting does not cause any
+ * ranges to overlap.
+ */
+void
+gtk_set_shift (GtkSet *set,
+ guint first,
+ int shift)
+{
+ int i;
+
+ for (i = 0; i < set->ranges->len; i++)
+ {
+ Range *r = &g_array_index (set->ranges, Range, i);
+ if (r->first >= first)
+ r->first += shift;
+ }
+}
+
+void
+gtk_set_iter_init (GtkSetIter *iter,
+ GtkSet *set)
+{
+ GtkRealSetIter *ri = (GtkRealSetIter *)iter;
+
+ ri->set = set;
+ ri->idx = -1;
+ ri->current = 0;
+}
+
+gboolean
+gtk_set_iter_next (GtkSetIter *iter,
+ guint *item)
+{
+ GtkRealSetIter *ri = (GtkRealSetIter *)iter;
+
+ if (ri->idx == -1)
+ {
+next_range:
+ ri->idx++;
+
+ if (ri->idx == ri->set->ranges->len)
+ return FALSE;
+
+ ri->current = &g_array_index (ri->set->ranges, Range, ri->idx);
+ ri->pos = ri->current->first;
+ }
+ else
+ {
+ ri->pos++;
+ if (ri->pos == ri->current->first + ri->current->n_items)
+ goto next_range;
+ }
+
+ *item = ri->pos;
+ return TRUE;
+}
+
+#if 0
+void
+gtk_set_dump (GtkSet *set)
+{
+ int i;
+
+ for (i = 0; i < set->ranges->len; i++)
+ {
+ Range *r = &g_array_index (set->ranges, Range, i);
+ g_print (" %u:%u", r->first, r->n_items);
+ }
+ g_print ("\n");
+}
+#endif