From 2000f47ae539ff31c248626e4d142a3221522708 Mon Sep 17 00:00:00 2001 From: Kurt Kartaltepe Date: Wed, 19 May 2021 00:34:09 -0700 Subject: [PATCH] [set] Compute is_subset by comparing pages. Test subsets one page at a time instead of by codepoints. On my machine this is about 250x faster than the previous implementation. --- src/hb-set.hh | 44 +++++++++++++++++++++++++++++++------ test/api/test-set.c | 53 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 7 deletions(-) diff --git a/src/hb-set.hh b/src/hb-set.hh index 9404ba261..28ece87ff 100644 --- a/src/hb-set.hh +++ b/src/hb-set.hh @@ -506,14 +506,44 @@ struct hb_set_t bool is_subset (const hb_set_t *larger_set) const { - if (get_population () > larger_set->get_population ()) - return false; + if (unlikely(larger_set->is_empty ())) + { + return is_empty (); + } - /* TODO Optimize to use pages. */ - hb_codepoint_t c = INVALID; - while (next (&c)) - if (!larger_set->has (c)) - return false; + uint32_t spi = 0; + for (uint32_t lpi = 0; spi < page_map.length && lpi < larger_set->page_map.length; lpi++) + { + uint32_t spm = page_map[spi].major; + uint32_t lpm = larger_set->page_map[lpi].major; + auto sp = page_at (spi); + auto lp = larger_set->page_at (lpi); + + if (spm < lpm && !sp.is_empty ()) + { + return false; + } + + if (lpm < spm) + { + continue; + } + + for (int j = 0; j < ARRAY_LENGTH_CONST (sp.v); j++) + { + if ((~lp.v[j] & sp.v[j]) != 0) { return false; } + } + + spi++; + } + + while (spi < page_map.length) + { + if (!page_at (spi++).is_empty ()) + { + return false; + } + } return true; } diff --git a/test/api/test-set.c b/test/api/test-set.c index 30a47674f..21a48ffa6 100644 --- a/test/api/test-set.c +++ b/test/api/test-set.c @@ -215,6 +215,58 @@ static void test_set_union (void) hb_set_destroy (u); } +static void +test_set_subsets (void) +{ + hb_set_t *s = hb_set_create (); + hb_set_t *l = hb_set_create (); + + hb_set_add (l, 0x0FFFF); + hb_set_add (s, 0x1FFFF); + g_assert (!hb_set_is_subset (s, l)); + hb_set_clear (s); + + hb_set_add (s, 0x0FFF0); + g_assert (!hb_set_is_subset (s, l)); + hb_set_clear (s); + + hb_set_add (s, 0x0AFFF); + g_assert (!hb_set_is_subset (s, l)); + + hb_set_clear (s); + g_assert (hb_set_is_subset (s, l)); + + hb_set_clear (l); + g_assert (hb_set_is_subset (s, l)); + + hb_set_add (s, 0x1FFFF); + g_assert (!hb_set_is_subset (s, l)); + hb_set_clear (s); + + hb_set_add (s, 0xFF); + hb_set_add (s, 0x1FFFF); + hb_set_add (s, 0x2FFFF); + + hb_set_add (l, 0xFF); + hb_set_add (l, 0x1FFFF); + hb_set_add (l, 0x2FFFF); + + g_assert (hb_set_is_subset (s, l)); + hb_set_del (l, 0xFF); + g_assert (!hb_set_is_subset (s, l)); + hb_set_add (l, 0xFF); + + hb_set_del (l, 0x2FFFF); + g_assert (!hb_set_is_subset (s, l)); + hb_set_add (l, 0x2FFFF); + + hb_set_del (l, 0x1FFFF); + g_assert (!hb_set_is_subset (s, l)); + + hb_set_destroy (s); + hb_set_destroy (l); +} + static void test_set_algebra (void) { @@ -528,6 +580,7 @@ main (int argc, char **argv) hb_test_init (&argc, &argv); hb_test_add (test_set_basic); + hb_test_add (test_set_subsets); hb_test_add (test_set_algebra); hb_test_add (test_set_iter); hb_test_add (test_set_empty);