summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBehdad Esfahbod <behdad@behdad.org>2018-02-14 01:00:10 -0800
committerBehdad Esfahbod <behdad@behdad.org>2018-02-14 01:00:10 -0800
commit694eaf636713b8d0bbe13f38c2553b1a2f3d2d3a (patch)
treefdbcb29553a9ba288de788d1812be23c9270a206
parentfe3bc524bd4f93bd67c13ed402720a13dd3484d3 (diff)
[set] Add backwards iterator
New API: - hb_set_previous() - hb_set_previous_range()
-rw-r--r--docs/harfbuzz-sections.txt2
-rw-r--r--src/hb-set-private.hh79
-rw-r--r--src/hb-set.cc49
-rw-r--r--src/hb-set.h19
-rw-r--r--test/api/test-set.c53
5 files changed, 194 insertions, 8 deletions
diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt
index 5df33a81..91faa0b7 100644
--- a/docs/harfbuzz-sections.txt
+++ b/docs/harfbuzz-sections.txt
@@ -528,7 +528,9 @@ hb_set_intersect
hb_set_is_empty
hb_set_is_equal
hb_set_next
+hb_set_previous
hb_set_next_range
+hb_set_previous_range
hb_set_reference
hb_set_set
hb_set_set_user_data
diff --git a/src/hb-set-private.hh b/src/hb-set-private.hh
index 45963487..7282b079 100644
--- a/src/hb-set-private.hh
+++ b/src/hb-set-private.hh
@@ -125,6 +125,33 @@ struct hb_set_t
*codepoint = i * ELT_BITS + j;
return true;
}
+ inline bool previous (hb_codepoint_t *codepoint) const
+ {
+ unsigned int m = (*codepoint - 1) & MASK;
+ if (m == MASK)
+ {
+ *codepoint = INVALID;
+ return false;
+ }
+ unsigned int i = m / ELT_BITS;
+ unsigned int j = m & ELT_MASK;
+
+ for (; (int) j >= 0; j--)
+ if (v[i] & (elt_t (1) << j))
+ goto found;
+ for (i--; (int) i >= 0; i--)
+ if (v[i])
+ for (j = ELT_BITS - 1; (int) j >= 0; j--)
+ if (v[i] & (elt_t (1) << j))
+ goto found;
+
+ *codepoint = INVALID;
+ return false;
+
+ found:
+ *codepoint = i * ELT_BITS + j;
+ return true;
+ }
inline hb_codepoint_t get_min (void) const
{
for (unsigned int i = 0; i < len (); i++)
@@ -476,7 +503,7 @@ struct hb_set_t
if (pages[page_map[i].index].next (codepoint))
{
*codepoint += page_map[i].major * page_t::PAGE_BITS;
- return true;
+ return true;
}
i++;
}
@@ -492,6 +519,37 @@ struct hb_set_t
*codepoint = INVALID;
return false;
}
+ inline bool previous (hb_codepoint_t *codepoint) const
+ {
+ if (unlikely (*codepoint == INVALID)) {
+ *codepoint = get_max ();
+ return *codepoint != INVALID;
+ }
+
+ page_map_t map = {get_major (*codepoint), 0};
+ unsigned int i;
+ page_map.bfind (map, &i);
+ if (i < page_map.len && page_map[i].major == map.major)
+ {
+ if (pages[page_map[i].index].previous (codepoint))
+ {
+ *codepoint += page_map[i].major * page_t::PAGE_BITS;
+ return true;
+ }
+ }
+ i--;
+ for (; (int) i >= 0; i--)
+ {
+ hb_codepoint_t m = pages[page_map[i].index].get_max ();
+ if (m != INVALID)
+ {
+ *codepoint = page_map[i].major * page_t::PAGE_BITS + m;
+ return true;
+ }
+ }
+ *codepoint = INVALID;
+ return false;
+ }
inline bool next_range (hb_codepoint_t *first, hb_codepoint_t *last) const
{
hb_codepoint_t i;
@@ -503,12 +561,31 @@ struct hb_set_t
return false;
}
+ /* TODO Speed up. */
*last = *first = i;
while (next (&i) && i == *last + 1)
(*last)++;
return true;
}
+ inline bool previous_range (hb_codepoint_t *first, hb_codepoint_t *last) const
+ {
+ hb_codepoint_t i;
+
+ i = *first;
+ if (!previous (&i))
+ {
+ *last = *first = INVALID;
+ return false;
+ }
+
+ /* TODO Speed up. */
+ *last = *first = i;
+ while (previous (&i) && i == *first - 1)
+ (*first)--;
+
+ return true;
+ }
inline unsigned int get_population (void) const
{
diff --git a/src/hb-set.cc b/src/hb-set.cc
index f3b864c1..07cf9d09 100644
--- a/src/hb-set.cc
+++ b/src/hb-set.cc
@@ -437,7 +437,9 @@ hb_set_get_max (const hb_set_t *set)
* @set: a set.
* @codepoint: (inout):
*
- *
+ * Gets the next number in @set that is greater than current value of @codepoint.
+ *
+ * Set @codepoint to %HB_SET_VALUE_INVALID to get started.
*
* Return value: whether there was a next value.
*
@@ -451,6 +453,26 @@ hb_set_next (const hb_set_t *set,
}
/**
+ * hb_set_previous:
+ * @set: a set.
+ * @codepoint: (inout):
+ *
+ * Gets the previous number in @set that is slower than current value of @codepoint.
+ *
+ * Set @codepoint to %HB_SET_VALUE_INVALID to get started.
+ *
+ * Return value: whether there was a previous value.
+ *
+ * Since: 1.8.0
+ **/
+hb_bool_t
+hb_set_previous (const hb_set_t *set,
+ hb_codepoint_t *codepoint)
+{
+ return set->previous (codepoint);
+}
+
+/**
* hb_set_next_range:
* @set: a set.
* @first: (out): output first codepoint in the range.
@@ -459,6 +481,8 @@ hb_set_next (const hb_set_t *set,
* Gets the next consecutive range of numbers in @set that
* are greater than current value of @last.
*
+ * Set @last to %HB_SET_VALUE_INVALID to get started.
+ *
* Return value: whether there was a next range.
*
* Since: 0.9.7
@@ -470,3 +494,26 @@ hb_set_next_range (const hb_set_t *set,
{
return set->next_range (first, last);
}
+
+/**
+ * hb_set_previous_range:
+ * @set: a set.
+ * @first: (inout): input current first and output first codepoint in the range.
+ * @last: (out): output last codepoint in the range.
+ *
+ * Gets the previous consecutive range of numbers in @set that
+ * are greater than current value of @last.
+ *
+ * Set @first to %HB_SET_VALUE_INVALID to get started.
+ *
+ * Return value: whether there was a previous range.
+ *
+ * Since: 1.8.0
+ **/
+hb_bool_t
+hb_set_previous_range (const hb_set_t *set,
+ hb_codepoint_t *first,
+ hb_codepoint_t *last)
+{
+ return set->previous_range (first, last);
+}
diff --git a/src/hb-set.h b/src/hb-set.h
index 2ce40607..b0f82f82 100644
--- a/src/hb-set.h
+++ b/src/hb-set.h
@@ -129,25 +129,36 @@ hb_set_symmetric_difference (hb_set_t *set,
HB_EXTERN unsigned int
hb_set_get_population (const hb_set_t *set);
-/* Returns -1 if set empty. */
+/* Returns HB_SET_VALUE_INVALID if set empty. */
HB_EXTERN hb_codepoint_t
hb_set_get_min (const hb_set_t *set);
-/* Returns -1 if set empty. */
+/* Returns HB_SET_VALUE_INVALID if set empty. */
HB_EXTERN hb_codepoint_t
hb_set_get_max (const hb_set_t *set);
-/* Pass -1 in to get started. */
+/* Pass HB_SET_VALUE_INVALID in to get started. */
HB_EXTERN hb_bool_t
hb_set_next (const hb_set_t *set,
hb_codepoint_t *codepoint);
-/* Pass -1 for first and last to get started. */
+/* Pass HB_SET_VALUE_INVALID in to get started. */
+HB_EXTERN hb_bool_t
+hb_set_previous (const hb_set_t *set,
+ hb_codepoint_t *codepoint);
+
+/* Pass HB_SET_VALUE_INVALID for first and last to get started. */
HB_EXTERN hb_bool_t
hb_set_next_range (const hb_set_t *set,
hb_codepoint_t *first,
hb_codepoint_t *last);
+/* Pass HB_SET_VALUE_INVALID for first and last to get started. */
+HB_EXTERN hb_bool_t
+hb_set_previous_range (const hb_set_t *set,
+ hb_codepoint_t *first,
+ hb_codepoint_t *last);
+
HB_END_DECLS
diff --git a/test/api/test-set.c b/test/api/test-set.c
index bbea2cf5..60e11d98 100644
--- a/test/api/test-set.c
+++ b/test/api/test-set.c
@@ -32,25 +32,33 @@
static void
test_empty (hb_set_t *s)
{
- hb_codepoint_t next = HB_SET_VALUE_INVALID;
+ hb_codepoint_t next;
g_assert_cmpint (hb_set_get_population (s), ==, 0);
g_assert_cmpint (hb_set_get_min (s), ==, HB_SET_VALUE_INVALID);
g_assert_cmpint (hb_set_get_max (s), ==, HB_SET_VALUE_INVALID);
g_assert (!hb_set_has (s, 13));
+ next = 53043;
g_assert (!hb_set_next (s, &next));
g_assert_cmpint (next, ==, HB_SET_VALUE_INVALID);
+ next = 07734;
+ g_assert (!hb_set_previous (s, &next));
+ g_assert_cmpint (next, ==, HB_SET_VALUE_INVALID);
g_assert (hb_set_is_empty (s));
}
static void
test_not_empty (hb_set_t *s)
{
- hb_codepoint_t next = HB_SET_VALUE_INVALID;
+ hb_codepoint_t next;
g_assert_cmpint (hb_set_get_population (s), !=, 0);
g_assert_cmpint (hb_set_get_min (s), !=, HB_SET_VALUE_INVALID);
g_assert_cmpint (hb_set_get_max (s), !=, HB_SET_VALUE_INVALID);
+ next = HB_SET_VALUE_INVALID;
g_assert (hb_set_next (s, &next));
g_assert_cmpint (next, !=, HB_SET_VALUE_INVALID);
+ next = HB_SET_VALUE_INVALID;
+ g_assert (hb_set_previous (s, &next));
+ g_assert_cmpint (next, !=, HB_SET_VALUE_INVALID);
}
static void
@@ -271,6 +279,27 @@ test_set_iter (void)
g_assert (!hb_set_next (s, &next));
g_assert_cmpint (next, ==, HB_SET_VALUE_INVALID);
+ next = HB_SET_VALUE_INVALID;
+ g_assert (hb_set_previous (s, &next));
+ g_assert_cmpint (next, ==, 20005);
+ g_assert (hb_set_previous (s, &next));
+ g_assert_cmpint (next, ==, 1200);
+ g_assert (hb_set_previous (s, &next));
+ g_assert_cmpint (next, ==, 1100);
+ g_assert (hb_set_previous (s, &next));
+ g_assert_cmpint (next, ==, 15);
+ g_assert (hb_set_previous (s, &next));
+ g_assert (hb_set_previous (s, &next));
+ g_assert_cmpint (next, ==, 13);
+ g_assert (hb_set_previous (s, &next));
+ g_assert (hb_set_previous (s, &next));
+ g_assert (hb_set_previous (s, &next));
+ g_assert_cmpint (next, ==, 10);
+ g_assert (hb_set_previous (s, &next));
+ g_assert_cmpint (next, ==, 6);
+ g_assert (!hb_set_previous (s, &next));
+ g_assert_cmpint (next, ==, HB_SET_VALUE_INVALID);
+
first = last = HB_SET_VALUE_INVALID;
g_assert (hb_set_next_range (s, &first, &last));
g_assert_cmpint (first, ==, 6);
@@ -291,6 +320,26 @@ test_set_iter (void)
g_assert_cmpint (first, ==, HB_SET_VALUE_INVALID);
g_assert_cmpint (last, ==, HB_SET_VALUE_INVALID);
+ first = last = HB_SET_VALUE_INVALID;
+ g_assert (hb_set_previous_range (s, &first, &last));
+ g_assert_cmpint (first, ==, 20005);
+ g_assert_cmpint (last, ==, 20005);
+ g_assert (hb_set_previous_range (s, &first, &last));
+ g_assert_cmpint (first, ==, 1200);
+ g_assert_cmpint (last, ==, 1200);
+ g_assert (hb_set_previous_range (s, &first, &last));
+ g_assert_cmpint (first, ==, 1100);
+ g_assert_cmpint (last, ==, 1100);
+ g_assert (hb_set_previous_range (s, &first, &last));
+ g_assert_cmpint (first, ==, 10);
+ g_assert_cmpint (last, ==, 15);
+ g_assert (hb_set_previous_range (s, &first, &last));
+ g_assert_cmpint (first, ==, 6);
+ g_assert_cmpint (last, ==, 6);
+ g_assert (!hb_set_previous_range (s, &first, &last));
+ g_assert_cmpint (first, ==, HB_SET_VALUE_INVALID);
+ g_assert_cmpint (last, ==, HB_SET_VALUE_INVALID);
+
hb_set_destroy (s);
}