Fix page_map corruption in hb_set_t during process(). If a process operation results in less pages then the current set has, it will likely corrupt the page_map since it overwrites page_map entries ahead of where it's processing. This fixes that problem by removing page_map entries that will be dropped. Then dropping orphaned pages and re-indexing retained pages.
diff --git a/src/hb-set.hh b/src/hb-set.hh index e60a0ee..9c1031f 100644 --- a/src/hb-set.hh +++ b/src/hb-set.hh
@@ -450,6 +450,32 @@ return true; } + void compact (unsigned int length) + { + hb_hashmap_t<uint32_t, uint32_t> old_index_to_page_map_index; + + for (unsigned int i = 0; i < length; i++) { + old_index_to_page_map_index.set (page_map[i].index, i); + } + + compact_pages (old_index_to_page_map_index); + } + + void compact_pages (const hb_hashmap_t<uint32_t, uint32_t>& old_index_to_page_map_index) + { + unsigned int write_index = 0; + for (unsigned int i = 0; i < pages.length; i++) + { + if (!old_index_to_page_map_index.has (i)) continue; + + if (write_index < i) + pages[write_index] = pages[i]; + + page_map[old_index_to_page_map_index[i]].index = write_index; + write_index++; + } + } + template <typename Op> void process (const Op& op, const hb_set_t *other) { @@ -463,10 +489,22 @@ unsigned int count = 0, newCount = 0; unsigned int a = 0, b = 0; + unsigned int write_index = 0; for (; a < na && b < nb; ) { if (page_map[a].major == other->page_map[b].major) { + if (!Op::passthru_left) + { + // Move page_map entries that we're keeping from the left side set + // to the front of the page_map vector. This isn't necessary if + // passthru_left is set since no left side pages will be removed + // in that case. + if (write_index < a) + page_map[write_index] = page_map[a]; + write_index++; + } + count++; a++; b++; @@ -489,9 +527,16 @@ if (Op::passthru_right) count += nb - b; - if (count > pages.length) - if (!resize (count)) - return; + if (!Op::passthru_left) + { + na = write_index; + next_page = write_index; + compact (write_index); + } + + if (!resize (count)) + return; + newCount = count; /* Process in-place backward. */
diff --git a/test/api/test-set.c b/test/api/test-set.c index aa2b388..d4e49f7 100644 --- a/test/api/test-set.c +++ b/test/api/test-set.c
@@ -135,6 +135,81 @@ // printf ("}\n"); // } +static void test_set_intersect_empty (void) +{ + hb_set_t* a = hb_set_create (); + hb_set_add (a, 3585); + hb_set_add (a, 21333); + hb_set_add (a, 24405); + + hb_set_t* b = hb_set_create(); + hb_set_add (b, 21483); + hb_set_add (b, 24064); + + hb_set_intersect (a, b); + g_assert (hb_set_is_empty (a)); + + hb_set_destroy (a); + hb_set_destroy (b); + + + a = hb_set_create (); + hb_set_add (a, 16777216); + + b = hb_set_create(); + hb_set_add (b, 0); + + hb_set_intersect (a, b); + g_assert (hb_set_is_empty (a)); + + hb_set_destroy (a); + hb_set_destroy (b); +} + +static void test_set_intersect_page_reduction (void) +{ + hb_set_t* a = hb_set_create (); + hb_set_add (a, 3585); + hb_set_add (a, 21333); + hb_set_add (a, 24405); + + hb_set_t* b = hb_set_create(); + hb_set_add (b, 3585); + hb_set_add (b, 24405); + + hb_set_intersect(a, b); + g_assert (hb_set_is_equal (a, b)); + + hb_set_destroy (a); + hb_set_destroy (b); +} + +static void test_set_union (void) +{ + hb_set_t* a = hb_set_create(); + hb_set_add (a, 3585); + hb_set_add (a, 21333); + hb_set_add (a, 24405); + + hb_set_t* b = hb_set_create(); + hb_set_add (b, 21483); + hb_set_add (b, 24064); + + hb_set_t* u = hb_set_create (); + hb_set_add (u, 3585); + hb_set_add (u, 21333); + hb_set_add (u, 21483); + hb_set_add (u, 24064); + hb_set_add (u, 24405); + + hb_set_union(b, a); + g_assert (hb_set_is_equal (u, b)); + + hb_set_destroy (a); + hb_set_destroy (b); + hb_set_destroy (u); +} + static void test_set_algebra (void) { @@ -405,5 +480,9 @@ hb_test_add (test_set_iter); hb_test_add (test_set_empty); + hb_test_add (test_set_intersect_empty); + hb_test_add (test_set_intersect_page_reduction); + hb_test_add (test_set_union); + return hb_test_run(); }