diff --git a/src/graph/pairpos-graph.hh b/src/graph/pairpos-graph.hh index 2f8df0b16..76684d2cc 100644 --- a/src/graph/pairpos-graph.hh +++ b/src/graph/pairpos-graph.hh @@ -295,8 +295,10 @@ struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4 do_split (split_context& split_context, - hb_vector_t split_points) + const hb_vector_t& split_points) { + // TODO(garretrieger): refactor into a common method shared between subtables. + // template on context which could provide the clone and shrink methods. hb_vector_t new_objects; if (!split_points) return new_objects; diff --git a/src/test-repacker.cc b/src/test-repacker.cc index 1b7e1f08b..eefb46efd 100644 --- a/src/test-repacker.cc +++ b/src/test-repacker.cc @@ -167,6 +167,29 @@ static unsigned add_coverage (char start, char end, return add_object (coverage, 10, c); } +static unsigned add_class_def (uint16_t class_count, + hb_serialize_context_t* c) +{ + uint8_t header[] = { + 0, 1, // format + 0, 5, // startGlyphID + (uint8_t) ((class_count >> 8) & 0xFF), + (uint8_t) (class_count & 0xFF), // count + }; + + start_object ((char*) header, 6, c); + for (uint16_t i = 1; i <= class_count; i++) + { + uint8_t class_value[] = { + (uint8_t) ((i >> 8) & 0xFF), + (uint8_t) (i & 0xFF), // count + }; + extend ((char*) class_value, 2, c); + } + + return c->pop_pack (false); +} + static unsigned add_pair_pos_1 (unsigned* pair_sets, char count, unsigned coverage, @@ -192,6 +215,57 @@ static unsigned add_pair_pos_1 (unsigned* pair_sets, return c->pop_pack (false); } +static unsigned add_pair_pos_2 (unsigned starting_class, + unsigned coverage, + unsigned class_def_1, uint16_t class_def_1_count, + unsigned class_def_2, uint16_t class_def_2_count, + hb_serialize_context_t* c) +{ + uint8_t format[] = { + 0, 2 + }; + + start_object ((char*) format, 2, c); + add_offset (coverage, c); + + constexpr unsigned num_values = 4; + uint8_t format1 = 0x01 | 0x02 | 0x08; + uint8_t format2 = 0x04; + uint8_t value_format[] = { + 0, format1, + 0, format2, + }; + extend ((char*) value_format, 4, c); + + add_offset (class_def_1, c); + add_offset (class_def_2, c); + + uint8_t class_counts[] = { + (uint8_t) ((class_def_1_count >> 8) & 0xFF), + (uint8_t) (class_def_1_count & 0xFF), + (uint8_t) ((class_def_2_count >> 8) & 0xFF), + (uint8_t) (class_def_2_count & 0xFF), + }; + extend ((char*) class_counts, 4, c); + + unsigned num_bytes_per_record = class_def_2_count * num_values * 2; + uint8_t* record = (uint8_t*) calloc (1, num_bytes_per_record); + for (uint16_t i = 0; i < class_def_1_count; i++) + { + + for (unsigned j = 0; j < num_bytes_per_record; j++) + { + record[j] = (uint8_t) (i + starting_class); + } + + extend((char*) record, num_bytes_per_record, c); + } + free (record); + + return c->pop_pack (false); +} + + static void run_resolve_overflow_test (const char* name, hb_serialize_context_t& overflowing, hb_serialize_context_t& expected, @@ -1108,6 +1182,67 @@ populate_serializer_with_large_pair_pos_1 (hb_serialize_context_t* c, c->end_serialize(); } +template +static void +populate_serializer_with_large_pair_pos_2 (hb_serialize_context_t* c, + bool as_extension = false) +{ + std::string large_string(60000, 'a'); + c->start_serialize (); + + unsigned coverage[num_pair_pos_2]; + unsigned class_def_1[num_pair_pos_2]; + unsigned class_def_2[num_pair_pos_2]; + unsigned pair_pos_2[num_pair_pos_2]; + + for (int i = num_pair_pos_2 - 1; i >= 0; i--) + { + if (num_class_2 >= num_class_1) + { + class_def_2[i] = add_class_def (num_class_2, c); + class_def_1[i] = add_class_def (num_class_1, c); + } else { + class_def_1[i] = add_class_def (num_class_1, c); + class_def_2[i] = add_class_def (num_class_2, c); + } + + + coverage[i] = add_coverage (5, + 5 + num_class_1 - 1, + c); + + pair_pos_2[i] = add_pair_pos_2 (i * num_class_1, + coverage[i], + class_def_1[i], num_class_1, + class_def_2[i], num_class_2, + c); + } + + unsigned pair_pos_1 = add_object (large_string.c_str(), 200, c); + + if (as_extension) { + + for (int i = num_pair_pos_2 - 1; i >= 0; i--) + pair_pos_2[i] = add_extension (pair_pos_2[i], 2, c); + pair_pos_1 = add_extension (pair_pos_1, 2, c); + } + + start_lookup (as_extension ? 9 : 2, 1 + num_pair_pos_2, c); + + add_offset (pair_pos_1, c); + for (int i = 0; i < num_pair_pos_2; i++) + add_offset (pair_pos_2[i], c); + + + unsigned lookup = finish_lookup (c); + + unsigned lookup_list = add_lookup_list (&lookup, 1, c); + + add_gsubgpos_header (lookup_list, c); + + c->end_serialize(); +} + static void test_sort_shortest () { size_t buffer_size = 100; @@ -1523,6 +1658,28 @@ static void test_resolve_with_extension_pair_pos_1_split () free (expected_buffer); } +static void test_resolve_with_basic_pair_pos_2_split () +{ + size_t buffer_size = 200000; + void* buffer = malloc (buffer_size); + assert (buffer); + hb_serialize_context_t c (buffer, buffer_size); + populate_serializer_with_large_pair_pos_2 <1, 4, 3000>(&c); + + void* expected_buffer = malloc (buffer_size); + assert (expected_buffer); + hb_serialize_context_t e (expected_buffer, buffer_size); + populate_serializer_with_large_pair_pos_2 <2, 2, 3000>(&e, true); + + run_resolve_overflow_test ("test_resolve_with_basic_pair_pos_2_split", + c, + e, + 20, + true, + HB_TAG('G', 'P', 'O', 'S')); + free (buffer); + free (expected_buffer); +} static void test_resolve_overflows_via_splitting_spaces () { @@ -1649,6 +1806,7 @@ test_shared_node_with_virtual_links () int main (int argc, char **argv) { + if (0) { test_serialize (); test_sort_shortest (); test_will_overflow_1 (); @@ -1673,6 +1831,9 @@ main (int argc, char **argv) test_resolve_with_extension_promotion (); test_resolve_with_basic_pair_pos_1_split (); test_resolve_with_extension_pair_pos_1_split (); + } + + test_resolve_with_basic_pair_pos_2_split (); // TODO(grieger): test with extensions already mixed in as well. // TODO(grieger): test two layer ext promotion setup.