From 694eaf636713b8d0bbe13f38c2553b1a2f3d2d3a Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Wed, 14 Feb 2018 01:00:10 -0800 Subject: [PATCH] [set] Add backwards iterator New API: - hb_set_previous() - hb_set_previous_range() --- docs/harfbuzz-sections.txt | 2 + src/hb-set-private.hh | 79 +++++++++++++++++++++++++++++++++++++- src/hb-set.cc | 49 ++++++++++++++++++++++- src/hb-set.h | 19 +++++++-- test/api/test-set.c | 53 ++++++++++++++++++++++++- 5 files changed, 194 insertions(+), 8 deletions(-) diff --git a/docs/harfbuzz-sections.txt b/docs/harfbuzz-sections.txt index 5df33a816..91faa0b75 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 459634872..7282b0799 100644 --- a/src/hb-set-private.hh +++ b/src/hb-set-private.hh @@ -121,6 +121,33 @@ struct hb_set_t *codepoint = INVALID; return false; + found: + *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; @@ -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 f3b864c12..07cf9d09b 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. * @@ -450,6 +452,26 @@ hb_set_next (const hb_set_t *set, return set->next (codepoint); } +/** + * 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. @@ -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 2ce406073..b0f82f822 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 bbea2cf5a..60e11d981 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); }