1 /* 2 * Copyright © 2022 Google, Inc. 3 * 4 * This is part of HarfBuzz, a text shaping library. 5 * 6 * Permission is hereby granted, without written agreement and without 7 * license or royalty fees, to use, copy, modify, and distribute this 8 * software and its documentation for any purpose, provided that the 9 * above copyright notice and the following two paragraphs appear in 10 * all copies of this software. 11 * 12 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR 13 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES 14 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN 15 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH 16 * DAMAGE. 17 * 18 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, 19 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND 20 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS 21 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO 22 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. 23 * 24 * Google Author(s): Garret Rieger 25 */ 26 27 #ifndef GRAPH_PAIRPOS_GRAPH_HH 28 #define GRAPH_PAIRPOS_GRAPH_HH 29 30 #include "split-helpers.hh" 31 #include "coverage-graph.hh" 32 #include "classdef-graph.hh" 33 #include "../OT/Layout/GPOS/PairPos.hh" 34 #include "../OT/Layout/GPOS/PosLookupSubTable.hh" 35 36 namespace graph { 37 38 struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes> 39 { sanitizegraph::PairPosFormat140 bool sanitize (graph_t::vertex_t& vertex) const 41 { 42 int64_t vertex_len = vertex.obj.tail - vertex.obj.head; 43 unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size; 44 if (vertex_len < min_size) return false; 45 46 return vertex_len >= 47 min_size + pairSet.get_size () - pairSet.len.get_size(); 48 } 49 split_subtablesgraph::PairPosFormat150 hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, 51 unsigned parent_index, 52 unsigned this_index) 53 { 54 hb_set_t visited; 55 56 const unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); 57 const unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); 58 const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size; 59 60 unsigned partial_coverage_size = 4; 61 unsigned accumulated = base_size; 62 hb_vector_t<unsigned> split_points; 63 for (unsigned i = 0; i < pairSet.len; i++) 64 { 65 unsigned pair_set_index = pair_set_graph_index (c, this_index, i); 66 unsigned accumulated_delta = 67 c.graph.find_subgraph_size (pair_set_index, visited) + 68 SmallTypes::size; // for PairSet offset. 69 partial_coverage_size += OT::HBUINT16::static_size; 70 71 accumulated += accumulated_delta; 72 unsigned total = accumulated + hb_min (partial_coverage_size, coverage_size); 73 74 if (total >= (1 << 16)) 75 { 76 split_points.push (i); 77 accumulated = base_size + accumulated_delta; 78 partial_coverage_size = 6; 79 visited.clear (); // node sharing isn't allowed between splits. 80 } 81 } 82 83 split_context_t split_context { 84 c, 85 this, 86 c.graph.duplicate_if_shared (parent_index, this_index), 87 }; 88 89 return actuate_subtable_split<split_context_t> (split_context, split_points); 90 } 91 92 private: 93 94 struct split_context_t { 95 gsubgpos_graph_context_t& c; 96 PairPosFormat1* thiz; 97 unsigned this_index; 98 original_countgraph::PairPosFormat1::split_context_t99 unsigned original_count () 100 { 101 return thiz->pairSet.len; 102 } 103 clone_rangegraph::PairPosFormat1::split_context_t104 unsigned clone_range (unsigned start, unsigned end) 105 { 106 return thiz->clone_range (this->c, this->this_index, start, end); 107 } 108 shrinkgraph::PairPosFormat1::split_context_t109 bool shrink (unsigned count) 110 { 111 return thiz->shrink (this->c, this->this_index, count); 112 } 113 }; 114 shrinkgraph::PairPosFormat1115 bool shrink (gsubgpos_graph_context_t& c, 116 unsigned this_index, 117 unsigned count) 118 { 119 DEBUG_MSG (SUBSET_REPACK, nullptr, 120 " Shrinking PairPosFormat1 (%u) to [0, %u).", 121 this_index, 122 count); 123 unsigned old_count = pairSet.len; 124 if (count >= old_count) 125 return true; 126 127 pairSet.len = count; 128 c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size; 129 130 auto coverage = c.graph.as_mutable_table<Coverage> (this_index, &this->coverage); 131 if (!coverage) return false; 132 133 unsigned coverage_size = coverage.vertex->table_size (); 134 auto new_coverage = 135 + hb_zip (coverage.table->iter (), hb_range ()) 136 | hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) { 137 return p.second < count; 138 }) 139 | hb_map_retains_sorting (hb_first) 140 ; 141 142 return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size); 143 } 144 145 // Create a new PairPos including PairSet's from start (inclusive) to end (exclusive). 146 // Returns object id of the new object. clone_rangegraph::PairPosFormat1147 unsigned clone_range (gsubgpos_graph_context_t& c, 148 unsigned this_index, 149 unsigned start, unsigned end) const 150 { 151 DEBUG_MSG (SUBSET_REPACK, nullptr, 152 " Cloning PairPosFormat1 (%u) range [%u, %u).", this_index, start, end); 153 154 unsigned num_pair_sets = end - start; 155 unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size 156 + num_pair_sets * SmallTypes::size; 157 158 unsigned pair_pos_prime_id = c.create_node (prime_size); 159 if (pair_pos_prime_id == (unsigned) -1) return -1; 160 161 PairPosFormat1* pair_pos_prime = (PairPosFormat1*) c.graph.object (pair_pos_prime_id).head; 162 pair_pos_prime->format = this->format; 163 pair_pos_prime->valueFormat[0] = this->valueFormat[0]; 164 pair_pos_prime->valueFormat[1] = this->valueFormat[1]; 165 pair_pos_prime->pairSet.len = num_pair_sets; 166 167 for (unsigned i = start; i < end; i++) 168 { 169 c.graph.move_child<> (this_index, 170 &pairSet[i], 171 pair_pos_prime_id, 172 &pair_pos_prime->pairSet[i - start]); 173 } 174 175 unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); 176 if (!Coverage::clone_coverage (c, 177 coverage_id, 178 pair_pos_prime_id, 179 2, 180 start, end)) 181 return -1; 182 183 return pair_pos_prime_id; 184 } 185 186 187 pair_set_graph_indexgraph::PairPosFormat1188 unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const 189 { 190 return c.graph.index_for_offset (this_index, &pairSet[i]); 191 } 192 }; 193 194 struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes> 195 { sanitizegraph::PairPosFormat2196 bool sanitize (graph_t::vertex_t& vertex) const 197 { 198 size_t vertex_len = vertex.table_size (); 199 unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size; 200 if (vertex_len < min_size) return false; 201 202 const unsigned class1_count = class1Count; 203 return vertex_len >= 204 min_size + class1_count * get_class1_record_size (); 205 } 206 split_subtablesgraph::PairPosFormat2207 hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, 208 unsigned parent_index, 209 unsigned this_index) 210 { 211 const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size; 212 const unsigned class_def_2_size = size_of (c, this_index, &classDef2); 213 const Coverage* coverage = get_coverage (c, this_index); 214 const ClassDef* class_def_1 = get_class_def_1 (c, this_index); 215 auto gid_and_class = 216 + coverage->iter () 217 | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { 218 return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1->get_class (gid)); 219 }) 220 ; 221 class_def_size_estimator_t estimator (gid_and_class); 222 223 const unsigned class1_count = class1Count; 224 const unsigned class2_count = class2Count; 225 const unsigned class1_record_size = get_class1_record_size (); 226 227 const unsigned value_1_len = valueFormat1.get_len (); 228 const unsigned value_2_len = valueFormat2.get_len (); 229 const unsigned total_value_len = value_1_len + value_2_len; 230 231 unsigned accumulated = base_size; 232 unsigned coverage_size = 4; 233 unsigned class_def_1_size = 4; 234 unsigned max_coverage_size = coverage_size; 235 unsigned max_class_def_1_size = class_def_1_size; 236 237 hb_vector_t<unsigned> split_points; 238 239 hb_hashmap_t<unsigned, unsigned> device_tables = get_all_device_tables (c, this_index); 240 hb_vector_t<unsigned> format1_device_table_indices = valueFormat1.get_device_table_indices (); 241 hb_vector_t<unsigned> format2_device_table_indices = valueFormat2.get_device_table_indices (); 242 bool has_device_tables = bool(format1_device_table_indices) || bool(format2_device_table_indices); 243 244 hb_set_t visited; 245 for (unsigned i = 0; i < class1_count; i++) 246 { 247 unsigned accumulated_delta = class1_record_size; 248 coverage_size += estimator.incremental_coverage_size (i); 249 class_def_1_size += estimator.incremental_class_def_size (i); 250 max_coverage_size = hb_max (max_coverage_size, coverage_size); 251 max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size); 252 253 if (has_device_tables) { 254 for (unsigned j = 0; j < class2_count; j++) 255 { 256 unsigned value1_index = total_value_len * (class2_count * i + j); 257 unsigned value2_index = value1_index + value_1_len; 258 accumulated_delta += size_of_value_record_children (c, 259 device_tables, 260 format1_device_table_indices, 261 value1_index, 262 visited); 263 accumulated_delta += size_of_value_record_children (c, 264 device_tables, 265 format2_device_table_indices, 266 value2_index, 267 visited); 268 } 269 } 270 271 accumulated += accumulated_delta; 272 unsigned total = accumulated 273 + coverage_size + class_def_1_size + class_def_2_size 274 // The largest object will pack last and can exceed the size limit. 275 - hb_max (hb_max (coverage_size, class_def_1_size), class_def_2_size); 276 if (total >= (1 << 16)) 277 { 278 split_points.push (i); 279 // split does not include i, so add the size for i when we reset the size counters. 280 accumulated = base_size + accumulated_delta; 281 coverage_size = 4 + estimator.incremental_coverage_size (i); 282 class_def_1_size = 4 + estimator.incremental_class_def_size (i); 283 visited.clear (); // node sharing isn't allowed between splits. 284 } 285 } 286 287 split_context_t split_context { 288 c, 289 this, 290 c.graph.duplicate_if_shared (parent_index, this_index), 291 class1_record_size, 292 total_value_len, 293 value_1_len, 294 value_2_len, 295 max_coverage_size, 296 max_class_def_1_size, 297 device_tables, 298 format1_device_table_indices, 299 format2_device_table_indices 300 }; 301 302 return actuate_subtable_split<split_context_t> (split_context, split_points); 303 } 304 private: 305 306 struct split_context_t 307 { 308 gsubgpos_graph_context_t& c; 309 PairPosFormat2* thiz; 310 unsigned this_index; 311 unsigned class1_record_size; 312 unsigned value_record_len; 313 unsigned value1_record_len; 314 unsigned value2_record_len; 315 unsigned max_coverage_size; 316 unsigned max_class_def_size; 317 318 const hb_hashmap_t<unsigned, unsigned>& device_tables; 319 const hb_vector_t<unsigned>& format1_device_table_indices; 320 const hb_vector_t<unsigned>& format2_device_table_indices; 321 original_countgraph::PairPosFormat2::split_context_t322 unsigned original_count () 323 { 324 return thiz->class1Count; 325 } 326 clone_rangegraph::PairPosFormat2::split_context_t327 unsigned clone_range (unsigned start, unsigned end) 328 { 329 return thiz->clone_range (*this, start, end); 330 } 331 shrinkgraph::PairPosFormat2::split_context_t332 bool shrink (unsigned count) 333 { 334 return thiz->shrink (*this, count); 335 } 336 }; 337 get_class1_record_sizegraph::PairPosFormat2338 size_t get_class1_record_size () const 339 { 340 const size_t class2_count = class2Count; 341 return 342 class2_count * (valueFormat1.get_size () + valueFormat2.get_size ()); 343 } 344 clone_rangegraph::PairPosFormat2345 unsigned clone_range (split_context_t& split_context, 346 unsigned start, unsigned end) const 347 { 348 DEBUG_MSG (SUBSET_REPACK, nullptr, 349 " Cloning PairPosFormat2 (%u) range [%u, %u).", split_context.this_index, start, end); 350 351 graph_t& graph = split_context.c.graph; 352 353 unsigned num_records = end - start; 354 unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size 355 + num_records * split_context.class1_record_size; 356 357 unsigned pair_pos_prime_id = split_context.c.create_node (prime_size); 358 if (pair_pos_prime_id == (unsigned) -1) return -1; 359 360 PairPosFormat2* pair_pos_prime = 361 (PairPosFormat2*) graph.object (pair_pos_prime_id).head; 362 pair_pos_prime->format = this->format; 363 pair_pos_prime->valueFormat1 = this->valueFormat1; 364 pair_pos_prime->valueFormat2 = this->valueFormat2; 365 pair_pos_prime->class1Count = num_records; 366 pair_pos_prime->class2Count = this->class2Count; 367 clone_class1_records (split_context, 368 pair_pos_prime_id, 369 start, 370 end); 371 372 unsigned coverage_id = 373 graph.index_for_offset (split_context.this_index, &coverage); 374 unsigned class_def_1_id = 375 graph.index_for_offset (split_context.this_index, &classDef1); 376 auto& coverage_v = graph.vertices_[coverage_id]; 377 auto& class_def_1_v = graph.vertices_[class_def_1_id]; 378 Coverage* coverage_table = (Coverage*) coverage_v.obj.head; 379 ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; 380 if (!coverage_table 381 || !coverage_table->sanitize (coverage_v) 382 || !class_def_1_table 383 || !class_def_1_table->sanitize (class_def_1_v)) 384 return -1; 385 386 auto klass_map = 387 + coverage_table->iter () 388 | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { 389 return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1_table->get_class (gid)); 390 }) 391 | hb_filter ([&] (hb_codepoint_t klass) { 392 return klass >= start && klass < end; 393 }, hb_second) 394 | hb_map_retains_sorting ([&] (hb_pair_t<hb_codepoint_t, hb_codepoint_t> gid_and_class) { 395 // Classes must be from 0...N so subtract start 396 return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid_and_class.first, gid_and_class.second - start); 397 }) 398 ; 399 400 if (!Coverage::add_coverage (split_context.c, 401 pair_pos_prime_id, 402 2, 403 + klass_map | hb_map_retains_sorting (hb_first), 404 split_context.max_coverage_size)) 405 return -1; 406 407 // classDef1 408 if (!ClassDef::add_class_def (split_context.c, 409 pair_pos_prime_id, 410 8, 411 + klass_map, 412 split_context.max_class_def_size)) 413 return -1; 414 415 // classDef2 416 unsigned class_def_2_id = 417 graph.index_for_offset (split_context.this_index, &classDef2); 418 auto* class_def_link = graph.vertices_[pair_pos_prime_id].obj.real_links.push (); 419 class_def_link->width = SmallTypes::size; 420 class_def_link->objidx = class_def_2_id; 421 class_def_link->position = 10; 422 graph.vertices_[class_def_2_id].parents.push (pair_pos_prime_id); 423 graph.duplicate (pair_pos_prime_id, class_def_2_id); 424 425 return pair_pos_prime_id; 426 } 427 clone_class1_recordsgraph::PairPosFormat2428 void clone_class1_records (split_context_t& split_context, 429 unsigned pair_pos_prime_id, 430 unsigned start, unsigned end) const 431 { 432 PairPosFormat2* pair_pos_prime = 433 (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; 434 435 char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size; 436 unsigned num_records = end - start; 437 hb_memcpy (&pair_pos_prime->values[0], 438 start_addr, 439 num_records * split_context.class1_record_size); 440 441 if (!split_context.format1_device_table_indices 442 && !split_context.format2_device_table_indices) 443 // No device tables to move over. 444 return; 445 446 unsigned class2_count = class2Count; 447 for (unsigned i = start; i < end; i++) 448 { 449 for (unsigned j = 0; j < class2_count; j++) 450 { 451 unsigned value1_index = split_context.value_record_len * (class2_count * i + j); 452 unsigned value2_index = value1_index + split_context.value1_record_len; 453 454 unsigned new_value1_index = split_context.value_record_len * (class2_count * (i - start) + j); 455 unsigned new_value2_index = new_value1_index + split_context.value1_record_len; 456 457 transfer_device_tables (split_context, 458 pair_pos_prime_id, 459 split_context.format1_device_table_indices, 460 value1_index, 461 new_value1_index); 462 463 transfer_device_tables (split_context, 464 pair_pos_prime_id, 465 split_context.format2_device_table_indices, 466 value2_index, 467 new_value2_index); 468 } 469 } 470 } 471 transfer_device_tablesgraph::PairPosFormat2472 void transfer_device_tables (split_context_t& split_context, 473 unsigned pair_pos_prime_id, 474 const hb_vector_t<unsigned>& device_table_indices, 475 unsigned old_value_record_index, 476 unsigned new_value_record_index) const 477 { 478 PairPosFormat2* pair_pos_prime = 479 (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; 480 481 for (unsigned i : device_table_indices) 482 { 483 OT::Offset16* record = (OT::Offset16*) &values[old_value_record_index + i]; 484 unsigned record_position = ((char*) record) - ((char*) this); 485 if (!split_context.device_tables.has (record_position)) continue; 486 487 split_context.c.graph.move_child ( 488 split_context.this_index, 489 record, 490 pair_pos_prime_id, 491 (OT::Offset16*) &pair_pos_prime->values[new_value_record_index + i]); 492 } 493 } 494 shrinkgraph::PairPosFormat2495 bool shrink (split_context_t& split_context, 496 unsigned count) 497 { 498 DEBUG_MSG (SUBSET_REPACK, nullptr, 499 " Shrinking PairPosFormat2 (%u) to [0, %u).", 500 split_context.this_index, 501 count); 502 unsigned old_count = class1Count; 503 if (count >= old_count) 504 return true; 505 506 graph_t& graph = split_context.c.graph; 507 class1Count = count; 508 graph.vertices_[split_context.this_index].obj.tail -= 509 (old_count - count) * split_context.class1_record_size; 510 511 auto coverage = 512 graph.as_mutable_table<Coverage> (split_context.this_index, &this->coverage); 513 if (!coverage) return false; 514 515 auto class_def_1 = 516 graph.as_mutable_table<ClassDef> (split_context.this_index, &classDef1); 517 if (!class_def_1) return false; 518 519 auto klass_map = 520 + coverage.table->iter () 521 | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { 522 return hb_pair_t<hb_codepoint_t, hb_codepoint_t> (gid, class_def_1.table->get_class (gid)); 523 }) 524 | hb_filter ([&] (hb_codepoint_t klass) { 525 return klass < count; 526 }, hb_second) 527 ; 528 529 auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first); 530 if (!Coverage::make_coverage (split_context.c, 531 + new_coverage, 532 coverage.index, 533 // existing ranges my not be kept, worst case size is a format 1 534 // coverage table. 535 4 + new_coverage.len() * 2)) 536 return false; 537 538 return ClassDef::make_class_def (split_context.c, 539 + klass_map, 540 class_def_1.index, 541 class_def_1.vertex->table_size ()); 542 } 543 544 hb_hashmap_t<unsigned, unsigned> get_all_device_tablesgraph::PairPosFormat2545 get_all_device_tables (gsubgpos_graph_context_t& c, 546 unsigned this_index) const 547 { 548 const auto& v = c.graph.vertices_[this_index]; 549 return v.position_to_index_map (); 550 } 551 get_coveragegraph::PairPosFormat2552 const Coverage* get_coverage (gsubgpos_graph_context_t& c, 553 unsigned this_index) const 554 { 555 unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); 556 auto& coverage_v = c.graph.vertices_[coverage_id]; 557 558 Coverage* coverage_table = (Coverage*) coverage_v.obj.head; 559 if (!coverage_table || !coverage_table->sanitize (coverage_v)) 560 return &Null(Coverage); 561 return coverage_table; 562 } 563 get_class_def_1graph::PairPosFormat2564 const ClassDef* get_class_def_1 (gsubgpos_graph_context_t& c, 565 unsigned this_index) const 566 { 567 unsigned class_def_1_id = c.graph.index_for_offset (this_index, &classDef1); 568 auto& class_def_1_v = c.graph.vertices_[class_def_1_id]; 569 570 ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; 571 if (!class_def_1_table || !class_def_1_table->sanitize (class_def_1_v)) 572 return &Null(ClassDef); 573 return class_def_1_table; 574 } 575 size_of_value_record_childrengraph::PairPosFormat2576 unsigned size_of_value_record_children (gsubgpos_graph_context_t& c, 577 const hb_hashmap_t<unsigned, unsigned>& device_tables, 578 const hb_vector_t<unsigned> device_table_indices, 579 unsigned value_record_index, 580 hb_set_t& visited) 581 { 582 unsigned size = 0; 583 for (unsigned i : device_table_indices) 584 { 585 OT::Layout::GPOS_impl::Value* record = &values[value_record_index + i]; 586 unsigned record_position = ((char*) record) - ((char*) this); 587 unsigned* obj_idx; 588 if (!device_tables.has (record_position, &obj_idx)) continue; 589 size += c.graph.find_subgraph_size (*obj_idx, visited); 590 } 591 return size; 592 } 593 size_ofgraph::PairPosFormat2594 unsigned size_of (gsubgpos_graph_context_t& c, 595 unsigned this_index, 596 const void* offset) const 597 { 598 const unsigned id = c.graph.index_for_offset (this_index, offset); 599 return c.graph.vertices_[id].table_size (); 600 } 601 }; 602 603 struct PairPos : public OT::Layout::GPOS_impl::PairPos 604 { split_subtablesgraph::PairPos605 hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, 606 unsigned parent_index, 607 unsigned this_index) 608 { 609 switch (u.format) { 610 case 1: 611 return ((PairPosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index); 612 case 2: 613 return ((PairPosFormat2*)(&u.format2))->split_subtables (c, parent_index, this_index); 614 #ifndef HB_NO_BEYOND_64K 615 case 3: HB_FALLTHROUGH; 616 case 4: HB_FALLTHROUGH; 617 // Don't split 24bit PairPos's. 618 #endif 619 default: 620 return hb_vector_t<unsigned> (); 621 } 622 } 623 sanitizegraph::PairPos624 bool sanitize (graph_t::vertex_t& vertex) const 625 { 626 int64_t vertex_len = vertex.obj.tail - vertex.obj.head; 627 if (vertex_len < u.format.get_size ()) return false; 628 629 switch (u.format) { 630 case 1: 631 return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); 632 case 2: 633 return ((PairPosFormat2*)(&u.format2))->sanitize (vertex); 634 #ifndef HB_NO_BEYOND_64K 635 case 3: HB_FALLTHROUGH; 636 case 4: HB_FALLTHROUGH; 637 #endif 638 default: 639 // We don't handle format 3 and 4 here. 640 return false; 641 } 642 } 643 }; 644 645 } 646 647 #endif // GRAPH_PAIRPOS_GRAPH_HH 648