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 #include "../hb-set.hh" 28 #include "../hb-priority-queue.hh" 29 #include "../hb-serialize.hh" 30 31 #ifndef GRAPH_GRAPH_HH 32 #define GRAPH_GRAPH_HH 33 34 namespace graph { 35 36 /** 37 * Represents a serialized table in the form of a graph. 38 * Provides methods for modifying and reordering the graph. 39 */ 40 struct graph_t 41 { 42 struct vertex_t 43 { 44 hb_serialize_context_t::object_t obj; 45 int64_t distance = 0 ; 46 unsigned space = 0 ; 47 unsigned start = 0; 48 unsigned end = 0; 49 unsigned priority = 0; 50 private: 51 unsigned incoming_edges_ = 0; 52 unsigned single_parent = (unsigned) -1; 53 hb_hashmap_t<unsigned, unsigned> parents; 54 public: 55 parents_itergraph::graph_t::vertex_t56 auto parents_iter () const HB_AUTO_RETURN 57 ( 58 hb_concat ( 59 hb_iter (&single_parent, single_parent != (unsigned) -1), 60 parents.keys_ref () 61 ) 62 ) 63 64 bool in_error () const 65 { 66 return parents.in_error (); 67 } 68 link_positions_validgraph::graph_t::vertex_t69 bool link_positions_valid (unsigned num_objects, bool removed_nil) 70 { 71 hb_set_t assigned_bytes; 72 for (const auto& l : obj.real_links) 73 { 74 if (l.objidx >= num_objects 75 || (removed_nil && !l.objidx)) 76 { 77 DEBUG_MSG (SUBSET_REPACK, nullptr, 78 "Invalid graph. Invalid object index."); 79 return false; 80 } 81 82 unsigned start = l.position; 83 unsigned end = start + l.width - 1; 84 85 if (unlikely (l.width < 2 || l.width > 4)) 86 { 87 DEBUG_MSG (SUBSET_REPACK, nullptr, 88 "Invalid graph. Invalid link width."); 89 return false; 90 } 91 92 if (unlikely (end >= table_size ())) 93 { 94 DEBUG_MSG (SUBSET_REPACK, nullptr, 95 "Invalid graph. Link position is out of bounds."); 96 return false; 97 } 98 99 if (unlikely (assigned_bytes.intersects (start, end))) 100 { 101 DEBUG_MSG (SUBSET_REPACK, nullptr, 102 "Invalid graph. Found offsets whose positions overlap."); 103 return false; 104 } 105 106 assigned_bytes.add_range (start, end); 107 } 108 109 return !assigned_bytes.in_error (); 110 } 111 normalizegraph::graph_t::vertex_t112 void normalize () 113 { 114 obj.real_links.qsort (); 115 for (auto& l : obj.real_links) 116 { 117 for (unsigned i = 0; i < l.width; i++) 118 { 119 obj.head[l.position + i] = 0; 120 } 121 } 122 } 123 equalsgraph::graph_t::vertex_t124 bool equals (const vertex_t& other, 125 const graph_t& graph, 126 const graph_t& other_graph, 127 unsigned depth) const 128 { 129 if (!(as_bytes () == other.as_bytes ())) 130 { 131 DEBUG_MSG (SUBSET_REPACK, nullptr, 132 "vertex [%lu] bytes != [%lu] bytes, depth = %u", 133 (unsigned long) table_size (), 134 (unsigned long) other.table_size (), 135 depth); 136 137 auto a = as_bytes (); 138 auto b = other.as_bytes (); 139 while (a || b) 140 { 141 DEBUG_MSG (SUBSET_REPACK, nullptr, 142 " 0x%x %s 0x%x", (unsigned) *a, (*a == *b) ? "==" : "!=", (unsigned) *b); 143 a++; 144 b++; 145 } 146 return false; 147 } 148 149 return links_equal (obj.real_links, other.obj.real_links, graph, other_graph, depth); 150 } 151 as_bytesgraph::graph_t::vertex_t152 hb_bytes_t as_bytes () const 153 { 154 return hb_bytes_t (obj.head, table_size ()); 155 } 156 swapgraph::graph_t157 friend void swap (vertex_t& a, vertex_t& b) 158 { 159 hb_swap (a.obj, b.obj); 160 hb_swap (a.distance, b.distance); 161 hb_swap (a.space, b.space); 162 hb_swap (a.single_parent, b.single_parent); 163 hb_swap (a.parents, b.parents); 164 hb_swap (a.incoming_edges_, b.incoming_edges_); 165 hb_swap (a.start, b.start); 166 hb_swap (a.end, b.end); 167 hb_swap (a.priority, b.priority); 168 } 169 170 hb_hashmap_t<unsigned, unsigned> position_to_index_mapgraph::graph_t::vertex_t171 position_to_index_map () const 172 { 173 hb_hashmap_t<unsigned, unsigned> result; 174 175 result.alloc (obj.real_links.length); 176 for (const auto& l : obj.real_links) { 177 result.set (l.position, l.objidx); 178 } 179 180 return result; 181 } 182 is_sharedgraph::graph_t::vertex_t183 bool is_shared () const 184 { 185 return parents.get_population () > 1; 186 } 187 incoming_edgesgraph::graph_t::vertex_t188 unsigned incoming_edges () const 189 { 190 if (HB_DEBUG_SUBSET_REPACK) 191 { 192 assert (incoming_edges_ == (single_parent != (unsigned) -1) + 193 (parents.values_ref () | hb_reduce (hb_add, 0))); 194 } 195 return incoming_edges_; 196 } 197 reset_parentsgraph::graph_t::vertex_t198 void reset_parents () 199 { 200 incoming_edges_ = 0; 201 single_parent = (unsigned) -1; 202 parents.reset (); 203 } 204 add_parentgraph::graph_t::vertex_t205 void add_parent (unsigned parent_index) 206 { 207 assert (parent_index != (unsigned) -1); 208 if (incoming_edges_ == 0) 209 { 210 single_parent = parent_index; 211 incoming_edges_ = 1; 212 return; 213 } 214 else if (single_parent != (unsigned) -1) 215 { 216 assert (incoming_edges_ == 1); 217 if (!parents.set (single_parent, 1)) 218 return; 219 single_parent = (unsigned) -1; 220 } 221 222 unsigned *v; 223 if (parents.has (parent_index, &v)) 224 { 225 (*v)++; 226 incoming_edges_++; 227 } 228 else if (parents.set (parent_index, 1)) 229 incoming_edges_++; 230 } 231 remove_parentgraph::graph_t::vertex_t232 void remove_parent (unsigned parent_index) 233 { 234 if (parent_index == single_parent) 235 { 236 single_parent = (unsigned) -1; 237 incoming_edges_--; 238 return; 239 } 240 241 unsigned *v; 242 if (parents.has (parent_index, &v)) 243 { 244 incoming_edges_--; 245 if (*v > 1) 246 (*v)--; 247 else 248 parents.del (parent_index); 249 250 if (incoming_edges_ == 1) 251 { 252 single_parent = *parents.keys (); 253 parents.reset (); 254 } 255 } 256 } 257 remove_real_linkgraph::graph_t::vertex_t258 void remove_real_link (unsigned child_index, const void* offset) 259 { 260 unsigned count = obj.real_links.length; 261 for (unsigned i = 0; i < count; i++) 262 { 263 auto& link = obj.real_links.arrayZ[i]; 264 if (link.objidx != child_index) 265 continue; 266 267 if ((obj.head + link.position) != offset) 268 continue; 269 270 obj.real_links.remove_unordered (i); 271 return; 272 } 273 } 274 remap_parentsgraph::graph_t::vertex_t275 bool remap_parents (const hb_vector_t<unsigned>& id_map) 276 { 277 if (single_parent != (unsigned) -1) 278 { 279 assert (single_parent < id_map.length); 280 single_parent = id_map[single_parent]; 281 return true; 282 } 283 284 hb_hashmap_t<unsigned, unsigned> new_parents; 285 new_parents.alloc (parents.get_population ()); 286 for (auto _ : parents) 287 { 288 assert (_.first < id_map.length); 289 assert (!new_parents.has (id_map[_.first])); 290 new_parents.set (id_map[_.first], _.second); 291 } 292 293 if (parents.in_error() || new_parents.in_error ()) 294 return false; 295 296 parents = std::move (new_parents); 297 return true; 298 } 299 remap_parentgraph::graph_t::vertex_t300 void remap_parent (unsigned old_index, unsigned new_index) 301 { 302 if (single_parent != (unsigned) -1) 303 { 304 if (single_parent == old_index) 305 single_parent = new_index; 306 return; 307 } 308 309 const unsigned *pv; 310 if (parents.has (old_index, &pv)) 311 { 312 unsigned v = *pv; 313 if (!parents.set (new_index, v)) 314 incoming_edges_ -= v; 315 parents.del (old_index); 316 317 if (incoming_edges_ == 1) 318 { 319 single_parent = *parents.keys (); 320 parents.reset (); 321 } 322 } 323 } 324 is_leafgraph::graph_t::vertex_t325 bool is_leaf () const 326 { 327 return !obj.real_links.length && !obj.virtual_links.length; 328 } 329 raise_prioritygraph::graph_t::vertex_t330 bool raise_priority () 331 { 332 if (has_max_priority ()) return false; 333 priority++; 334 return true; 335 } 336 has_max_prioritygraph::graph_t::vertex_t337 bool has_max_priority () const { 338 return priority >= 3; 339 } 340 table_sizegraph::graph_t::vertex_t341 size_t table_size () const { 342 return obj.tail - obj.head; 343 } 344 modified_distancegraph::graph_t::vertex_t345 int64_t modified_distance (unsigned order) const 346 { 347 // TODO(garretrieger): once priority is high enough, should try 348 // setting distance = 0 which will force to sort immediately after 349 // it's parent where possible. 350 351 int64_t modified_distance = 352 hb_min (hb_max(distance + distance_modifier (), 0), 0x7FFFFFFFFFF); 353 if (has_max_priority ()) { 354 modified_distance = 0; 355 } 356 return (modified_distance << 18) | (0x003FFFF & order); 357 } 358 distance_modifiergraph::graph_t::vertex_t359 int64_t distance_modifier () const 360 { 361 if (!priority) return 0; 362 int64_t table_size = obj.tail - obj.head; 363 364 if (priority == 1) 365 return -table_size / 2; 366 367 return -table_size; 368 } 369 370 private: links_equalgraph::graph_t::vertex_t371 bool links_equal (const hb_vector_t<hb_serialize_context_t::object_t::link_t>& this_links, 372 const hb_vector_t<hb_serialize_context_t::object_t::link_t>& other_links, 373 const graph_t& graph, 374 const graph_t& other_graph, 375 unsigned depth) const 376 { 377 auto a = this_links.iter (); 378 auto b = other_links.iter (); 379 380 while (a && b) 381 { 382 const auto& link_a = *a; 383 const auto& link_b = *b; 384 385 if (link_a.width != link_b.width || 386 link_a.is_signed != link_b.is_signed || 387 link_a.whence != link_b.whence || 388 link_a.position != link_b.position || 389 link_a.bias != link_b.bias) 390 return false; 391 392 if (!graph.vertices_[link_a.objidx].equals ( 393 other_graph.vertices_[link_b.objidx], graph, other_graph, depth + 1)) 394 return false; 395 396 a++; 397 b++; 398 } 399 400 if (bool (a) != bool (b)) 401 return false; 402 403 return true; 404 } 405 }; 406 407 template <typename T> 408 struct vertex_and_table_t 409 { vertex_and_table_tgraph::graph_t::vertex_and_table_t410 vertex_and_table_t () : index (0), vertex (nullptr), table (nullptr) 411 {} 412 413 unsigned index; 414 vertex_t* vertex; 415 T* table; 416 operator boolgraph::graph_t::vertex_and_table_t417 operator bool () { 418 return table && vertex; 419 } 420 }; 421 422 /* 423 * A topological sorting of an object graph. Ordered 424 * in reverse serialization order (first object in the 425 * serialization is at the end of the list). This matches 426 * the 'packed' object stack used internally in the 427 * serializer 428 */ 429 template<typename T> graph_tgraph::graph_t430 graph_t (const T& objects) 431 : parents_invalid (true), 432 distance_invalid (true), 433 positions_invalid (true), 434 successful (true), 435 buffers () 436 { 437 num_roots_for_space_.push (1); 438 bool removed_nil = false; 439 vertices_.alloc (objects.length); 440 vertices_scratch_.alloc (objects.length); 441 unsigned count = objects.length; 442 for (unsigned i = 0; i < count; i++) 443 { 444 // If this graph came from a serialization buffer object 0 is the 445 // nil object. We don't need it for our purposes here so drop it. 446 if (i == 0 && !objects.arrayZ[i]) 447 { 448 removed_nil = true; 449 continue; 450 } 451 452 vertex_t* v = vertices_.push (); 453 if (check_success (!vertices_.in_error ())) 454 v->obj = *objects.arrayZ[i]; 455 456 check_success (v->link_positions_valid (count, removed_nil)); 457 458 if (!removed_nil) continue; 459 // Fix indices to account for removed nil object. 460 for (auto& l : v->obj.all_links_writer ()) { 461 l.objidx--; 462 } 463 } 464 } 465 ~graph_tgraph::graph_t466 ~graph_t () 467 { 468 for (char* b : buffers) 469 hb_free (b); 470 } 471 operator ==graph::graph_t472 bool operator== (const graph_t& other) const 473 { 474 return root ().equals (other.root (), *this, other, 0); 475 } 476 printgraph::graph_t477 void print () const { 478 for (int i = vertices_.length - 1; i >= 0; i--) 479 { 480 const auto& v = vertices_[i]; 481 printf("%d: %u [", i, (unsigned int)v.table_size()); 482 for (const auto &l : v.obj.real_links) { 483 printf("%u, ", l.objidx); 484 } 485 printf("]\n"); 486 } 487 } 488 489 // Sorts links of all objects in a consistent manner and zeroes all offsets. normalizegraph::graph_t490 void normalize () 491 { 492 for (auto& v : vertices_.writer ()) 493 v.normalize (); 494 } 495 in_errorgraph::graph_t496 bool in_error () const 497 { 498 return !successful || 499 vertices_.in_error () || 500 num_roots_for_space_.in_error (); 501 } 502 rootgraph::graph_t503 const vertex_t& root () const 504 { 505 return vertices_[root_idx ()]; 506 } 507 root_idxgraph::graph_t508 unsigned root_idx () const 509 { 510 // Object graphs are in reverse order, the first object is at the end 511 // of the vector. Since the graph is topologically sorted it's safe to 512 // assume the first object has no incoming edges. 513 return vertices_.length - 1; 514 } 515 objectgraph::graph_t516 const hb_serialize_context_t::object_t& object (unsigned i) const 517 { 518 return vertices_[i].obj; 519 } 520 add_buffergraph::graph_t521 bool add_buffer (char* buffer) 522 { 523 buffers.push (buffer); 524 return !buffers.in_error (); 525 } 526 527 /* 528 * Adds a 16 bit link from parent_id to child_id 529 */ 530 template<typename T> add_linkgraph::graph_t531 void add_link (T* offset, 532 unsigned parent_id, 533 unsigned child_id) 534 { 535 auto& v = vertices_[parent_id]; 536 auto* link = v.obj.real_links.push (); 537 link->width = 2; 538 link->objidx = child_id; 539 link->position = (char*) offset - (char*) v.obj.head; 540 vertices_[child_id].add_parent (parent_id); 541 } 542 543 /* 544 * Generates a new topological sorting of graph ordered by the shortest 545 * distance to each node if positions are marked as invalid. 546 */ sort_shortest_distance_if_neededgraph::graph_t547 void sort_shortest_distance_if_needed () 548 { 549 if (!positions_invalid) return; 550 sort_shortest_distance (); 551 } 552 553 554 /* 555 * Generates a new topological sorting of graph ordered by the shortest 556 * distance to each node. 557 */ sort_shortest_distancegraph::graph_t558 void sort_shortest_distance () 559 { 560 positions_invalid = true; 561 562 if (vertices_.length <= 1) { 563 // Graph of 1 or less doesn't need sorting. 564 return; 565 } 566 567 update_distances (); 568 569 hb_priority_queue_t<int64_t> queue; 570 hb_vector_t<vertex_t> &sorted_graph = vertices_scratch_; 571 if (unlikely (!check_success (sorted_graph.resize (vertices_.length)))) return; 572 hb_vector_t<unsigned> id_map; 573 if (unlikely (!check_success (id_map.resize (vertices_.length)))) return; 574 575 hb_vector_t<unsigned> removed_edges; 576 if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return; 577 update_parents (); 578 579 queue.insert (root ().modified_distance (0), root_idx ()); 580 int new_id = root_idx (); 581 unsigned order = 1; 582 while (!queue.in_error () && !queue.is_empty ()) 583 { 584 unsigned next_id = queue.pop_minimum().second; 585 586 sorted_graph[new_id] = std::move (vertices_[next_id]); 587 const vertex_t& next = sorted_graph[new_id]; 588 589 if (unlikely (!check_success(new_id >= 0))) { 590 // We are out of ids. Which means we've visited a node more than once. 591 // This graph contains a cycle which is not allowed. 592 DEBUG_MSG (SUBSET_REPACK, nullptr, "Invalid graph. Contains cycle."); 593 return; 594 } 595 596 id_map[next_id] = new_id--; 597 598 for (const auto& link : next.obj.all_links ()) { 599 removed_edges[link.objidx]++; 600 if (!(vertices_[link.objidx].incoming_edges () - removed_edges[link.objidx])) 601 // Add the order that the links were encountered to the priority. 602 // This ensures that ties between priorities objects are broken in a consistent 603 // way. More specifically this is set up so that if a set of objects have the same 604 // distance they'll be added to the topological order in the order that they are 605 // referenced from the parent object. 606 queue.insert (vertices_[link.objidx].modified_distance (order++), 607 link.objidx); 608 } 609 } 610 611 check_success (!queue.in_error ()); 612 check_success (!sorted_graph.in_error ()); 613 614 check_success (remap_all_obj_indices (id_map, &sorted_graph)); 615 vertices_ = std::move (sorted_graph); 616 617 if (!check_success (new_id == -1)) 618 print_orphaned_nodes (); 619 } 620 621 /* 622 * Finds the set of nodes (placed into roots) that should be assigned unique spaces. 623 * More specifically this looks for the top most 24 bit or 32 bit links in the graph. 624 * Some special casing is done that is specific to the layout of GSUB/GPOS tables. 625 */ find_space_rootsgraph::graph_t626 void find_space_roots (hb_set_t& visited, hb_set_t& roots) 627 { 628 int root_index = (int) root_idx (); 629 for (int i = root_index; i >= 0; i--) 630 { 631 if (visited.has (i)) continue; 632 633 // Only real links can form 32 bit spaces 634 for (auto& l : vertices_[i].obj.real_links) 635 { 636 if (l.is_signed || l.width < 3) 637 continue; 638 639 if (i == root_index && l.width == 3) 640 // Ignore 24bit links from the root node, this skips past the single 24bit 641 // pointer to the lookup list. 642 continue; 643 644 if (l.width == 3) 645 { 646 // A 24bit offset forms a root, unless there is 32bit offsets somewhere 647 // in it's subgraph, then those become the roots instead. This is to make sure 648 // that extension subtables beneath a 24bit lookup become the spaces instead 649 // of the offset to the lookup. 650 hb_set_t sub_roots; 651 find_32bit_roots (l.objidx, sub_roots); 652 if (sub_roots) { 653 for (unsigned sub_root_idx : sub_roots) { 654 roots.add (sub_root_idx); 655 find_subgraph (sub_root_idx, visited); 656 } 657 continue; 658 } 659 } 660 661 roots.add (l.objidx); 662 find_subgraph (l.objidx, visited); 663 } 664 } 665 } 666 667 template <typename T, typename ...Ts> as_tablegraph::graph_t668 vertex_and_table_t<T> as_table (unsigned parent, const void* offset, Ts... ds) 669 { 670 return as_table_from_index<T> (index_for_offset (parent, offset), std::forward<Ts>(ds)...); 671 } 672 673 template <typename T, typename ...Ts> as_mutable_tablegraph::graph_t674 vertex_and_table_t<T> as_mutable_table (unsigned parent, const void* offset, Ts... ds) 675 { 676 return as_table_from_index<T> (mutable_index_for_offset (parent, offset), std::forward<Ts>(ds)...); 677 } 678 679 template <typename T, typename ...Ts> as_table_from_indexgraph::graph_t680 vertex_and_table_t<T> as_table_from_index (unsigned index, Ts... ds) 681 { 682 if (index >= vertices_.length) 683 return vertex_and_table_t<T> (); 684 685 vertex_and_table_t<T> r; 686 r.vertex = &vertices_[index]; 687 r.table = (T*) r.vertex->obj.head; 688 r.index = index; 689 if (!r.table) 690 return vertex_and_table_t<T> (); 691 692 if (!r.table->sanitize (*(r.vertex), std::forward<Ts>(ds)...)) 693 return vertex_and_table_t<T> (); 694 695 return r; 696 } 697 698 // Finds the object id of the object pointed to by the offset at 'offset' 699 // within object[node_idx]. index_for_offsetgraph::graph_t700 unsigned index_for_offset (unsigned node_idx, const void* offset) const 701 { 702 const auto& node = object (node_idx); 703 if (offset < node.head || offset >= node.tail) return -1; 704 705 unsigned count = node.real_links.length; 706 for (unsigned i = 0; i < count; i++) 707 { 708 // Use direct access for increased performance, this is a hot method. 709 const auto& link = node.real_links.arrayZ[i]; 710 if (offset != node.head + link.position) 711 continue; 712 return link.objidx; 713 } 714 715 return -1; 716 } 717 718 // Finds the object id of the object pointed to by the offset at 'offset' 719 // within object[node_idx]. Ensures that the returned object is safe to mutate. 720 // That is, if the original child object is shared by parents other than node_idx 721 // it will be duplicated and the duplicate will be returned instead. mutable_index_for_offsetgraph::graph_t722 unsigned mutable_index_for_offset (unsigned node_idx, const void* offset) 723 { 724 unsigned child_idx = index_for_offset (node_idx, offset); 725 auto& child = vertices_[child_idx]; 726 for (unsigned p : child.parents_iter ()) 727 { 728 if (p != node_idx) { 729 return duplicate (node_idx, child_idx); 730 } 731 } 732 733 return child_idx; 734 } 735 736 737 /* 738 * Assign unique space numbers to each connected subgraph of 24 bit and/or 32 bit offset(s). 739 * Currently, this is implemented specifically tailored to the structure of a GPOS/GSUB 740 * (including with 24bit offsets) table. 741 */ assign_spacesgraph::graph_t742 bool assign_spaces () 743 { 744 update_parents (); 745 746 hb_set_t visited; 747 hb_set_t roots; 748 find_space_roots (visited, roots); 749 750 // Mark everything not in the subgraphs of the roots as visited. This prevents 751 // subgraphs from being connected via nodes not in those subgraphs. 752 visited.invert (); 753 754 if (!roots) return false; 755 756 while (roots) 757 { 758 uint32_t next = HB_SET_VALUE_INVALID; 759 if (unlikely (!check_success (!roots.in_error ()))) break; 760 if (!roots.next (&next)) break; 761 762 hb_set_t connected_roots; 763 find_connected_nodes (next, roots, visited, connected_roots); 764 if (unlikely (!check_success (!connected_roots.in_error ()))) break; 765 766 isolate_subgraph (connected_roots); 767 if (unlikely (!check_success (!connected_roots.in_error ()))) break; 768 769 unsigned next_space = this->next_space (); 770 num_roots_for_space_.push (0); 771 for (unsigned root : connected_roots) 772 { 773 DEBUG_MSG (SUBSET_REPACK, nullptr, "Subgraph %u gets space %u", root, next_space); 774 vertices_[root].space = next_space; 775 num_roots_for_space_[next_space] = num_roots_for_space_[next_space] + 1; 776 distance_invalid = true; 777 positions_invalid = true; 778 } 779 780 // TODO(grieger): special case for GSUB/GPOS use extension promotions to move 16 bit space 781 // into the 32 bit space as needed, instead of using isolation. 782 } 783 784 785 786 return true; 787 } 788 789 /* 790 * Isolates the subgraph of nodes reachable from root. Any links to nodes in the subgraph 791 * that originate from outside of the subgraph will be removed by duplicating the linked to 792 * object. 793 * 794 * Indices stored in roots will be updated if any of the roots are duplicated to new indices. 795 */ isolate_subgraphgraph::graph_t796 bool isolate_subgraph (hb_set_t& roots) 797 { 798 update_parents (); 799 hb_map_t subgraph; 800 801 // incoming edges to root_idx should be all 32 bit in length so we don't need to de-dup these 802 // set the subgraph incoming edge count to match all of root_idx's incoming edges 803 hb_set_t parents; 804 for (unsigned root_idx : roots) 805 { 806 subgraph.set (root_idx, wide_parents (root_idx, parents)); 807 find_subgraph (root_idx, subgraph); 808 } 809 if (subgraph.in_error ()) 810 return false; 811 812 unsigned original_root_idx = root_idx (); 813 hb_map_t index_map; 814 bool made_changes = false; 815 for (auto entry : subgraph.iter ()) 816 { 817 assert (entry.first < vertices_.length); 818 const auto& node = vertices_[entry.first]; 819 unsigned subgraph_incoming_edges = entry.second; 820 821 if (subgraph_incoming_edges < node.incoming_edges ()) 822 { 823 // Only de-dup objects with incoming links from outside the subgraph. 824 made_changes = true; 825 duplicate_subgraph (entry.first, index_map); 826 } 827 } 828 829 if (in_error ()) 830 return false; 831 832 if (!made_changes) 833 return false; 834 835 if (original_root_idx != root_idx () 836 && parents.has (original_root_idx)) 837 { 838 // If the root idx has changed since parents was determined, update root idx in parents 839 parents.add (root_idx ()); 840 parents.del (original_root_idx); 841 } 842 843 auto new_subgraph = 844 + subgraph.keys () 845 | hb_map([&] (uint32_t node_idx) { 846 const uint32_t *v; 847 if (index_map.has (node_idx, &v)) return *v; 848 return node_idx; 849 }) 850 ; 851 852 remap_obj_indices (index_map, new_subgraph); 853 remap_obj_indices (index_map, parents.iter (), true); 854 855 // Update roots set with new indices as needed. 856 for (auto next : roots) 857 { 858 const uint32_t *v; 859 if (index_map.has (next, &v)) 860 { 861 roots.del (next); 862 roots.add (*v); 863 } 864 } 865 866 return true; 867 } 868 find_subgraphgraph::graph_t869 void find_subgraph (unsigned node_idx, hb_map_t& subgraph) 870 { 871 for (const auto& link : vertices_[node_idx].obj.all_links ()) 872 { 873 hb_codepoint_t *v; 874 if (subgraph.has (link.objidx, &v)) 875 { 876 (*v)++; 877 continue; 878 } 879 subgraph.set (link.objidx, 1); 880 find_subgraph (link.objidx, subgraph); 881 } 882 } 883 find_subgraphgraph::graph_t884 void find_subgraph (unsigned node_idx, hb_set_t& subgraph) 885 { 886 if (subgraph.has (node_idx)) return; 887 subgraph.add (node_idx); 888 for (const auto& link : vertices_[node_idx].obj.all_links ()) 889 find_subgraph (link.objidx, subgraph); 890 } 891 find_subgraph_sizegraph::graph_t892 size_t find_subgraph_size (unsigned node_idx, hb_set_t& subgraph, unsigned max_depth = -1) 893 { 894 if (subgraph.has (node_idx)) return 0; 895 subgraph.add (node_idx); 896 897 const auto& o = vertices_[node_idx].obj; 898 size_t size = o.tail - o.head; 899 if (max_depth == 0) 900 return size; 901 902 for (const auto& link : o.all_links ()) 903 size += find_subgraph_size (link.objidx, subgraph, max_depth - 1); 904 return size; 905 } 906 907 /* 908 * Finds the topmost children of 32bit offsets in the subgraph starting 909 * at node_idx. Found indices are placed into 'found'. 910 */ find_32bit_rootsgraph::graph_t911 void find_32bit_roots (unsigned node_idx, hb_set_t& found) 912 { 913 for (const auto& link : vertices_[node_idx].obj.all_links ()) 914 { 915 if (!link.is_signed && link.width == 4) { 916 found.add (link.objidx); 917 continue; 918 } 919 find_32bit_roots (link.objidx, found); 920 } 921 } 922 923 /* 924 * Moves the child of old_parent_idx pointed to by old_offset to a new 925 * vertex at the new_offset. 926 */ 927 template<typename O> move_childgraph::graph_t928 void move_child (unsigned old_parent_idx, 929 const O* old_offset, 930 unsigned new_parent_idx, 931 const O* new_offset) 932 { 933 distance_invalid = true; 934 positions_invalid = true; 935 936 auto& old_v = vertices_[old_parent_idx]; 937 auto& new_v = vertices_[new_parent_idx]; 938 939 unsigned child_id = index_for_offset (old_parent_idx, 940 old_offset); 941 942 auto* new_link = new_v.obj.real_links.push (); 943 new_link->width = O::static_size; 944 new_link->objidx = child_id; 945 new_link->position = (const char*) new_offset - (const char*) new_v.obj.head; 946 947 auto& child = vertices_[child_id]; 948 child.add_parent (new_parent_idx); 949 950 old_v.remove_real_link (child_id, old_offset); 951 child.remove_parent (old_parent_idx); 952 } 953 954 /* 955 * duplicates all nodes in the subgraph reachable from node_idx. Does not re-assign 956 * links. index_map is updated with mappings from old id to new id. If a duplication has already 957 * been performed for a given index, then it will be skipped. 958 */ duplicate_subgraphgraph::graph_t959 void duplicate_subgraph (unsigned node_idx, hb_map_t& index_map) 960 { 961 if (index_map.has (node_idx)) 962 return; 963 964 unsigned clone_idx = duplicate (node_idx); 965 if (!check_success (clone_idx != (unsigned) -1)) 966 return; 967 968 index_map.set (node_idx, clone_idx); 969 for (const auto& l : object (node_idx).all_links ()) { 970 duplicate_subgraph (l.objidx, index_map); 971 } 972 } 973 974 /* 975 * Creates a copy of node_idx and returns it's new index. 976 */ duplicategraph::graph_t977 unsigned duplicate (unsigned node_idx) 978 { 979 positions_invalid = true; 980 distance_invalid = true; 981 982 auto* clone = vertices_.push (); 983 auto& child = vertices_[node_idx]; 984 if (vertices_.in_error ()) { 985 return -1; 986 } 987 988 clone->obj.head = child.obj.head; 989 clone->obj.tail = child.obj.tail; 990 clone->distance = child.distance; 991 clone->space = child.space; 992 clone->reset_parents (); 993 994 unsigned clone_idx = vertices_.length - 2; 995 for (const auto& l : child.obj.real_links) 996 { 997 clone->obj.real_links.push (l); 998 vertices_[l.objidx].add_parent (clone_idx); 999 } 1000 for (const auto& l : child.obj.virtual_links) 1001 { 1002 clone->obj.virtual_links.push (l); 1003 vertices_[l.objidx].add_parent (clone_idx); 1004 } 1005 1006 check_success (!clone->obj.real_links.in_error ()); 1007 check_success (!clone->obj.virtual_links.in_error ()); 1008 1009 // The last object is the root of the graph, so swap back the root to the end. 1010 // The root's obj idx does change, however since it's root nothing else refers to it. 1011 // all other obj idx's will be unaffected. 1012 hb_swap (vertices_[vertices_.length - 2], *clone); 1013 1014 // Since the root moved, update the parents arrays of all children on the root. 1015 for (const auto& l : root ().obj.all_links ()) 1016 vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); 1017 1018 return clone_idx; 1019 } 1020 1021 /* 1022 * Creates a copy of child and re-assigns the link from 1023 * parent to the clone. The copy is a shallow copy, objects 1024 * linked from child are not duplicated. 1025 */ duplicate_if_sharedgraph::graph_t1026 unsigned duplicate_if_shared (unsigned parent_idx, unsigned child_idx) 1027 { 1028 unsigned new_idx = duplicate (parent_idx, child_idx); 1029 if (new_idx == (unsigned) -1) return child_idx; 1030 return new_idx; 1031 } 1032 1033 1034 /* 1035 * Creates a copy of child and re-assigns the link from 1036 * parent to the clone. The copy is a shallow copy, objects 1037 * linked from child are not duplicated. 1038 */ duplicategraph::graph_t1039 unsigned duplicate (unsigned parent_idx, unsigned child_idx) 1040 { 1041 update_parents (); 1042 1043 unsigned links_to_child = 0; 1044 for (const auto& l : vertices_[parent_idx].obj.all_links ()) 1045 { 1046 if (l.objidx == child_idx) links_to_child++; 1047 } 1048 1049 if (vertices_[child_idx].incoming_edges () <= links_to_child) 1050 { 1051 // Can't duplicate this node, doing so would orphan the original one as all remaining links 1052 // to child are from parent. 1053 DEBUG_MSG (SUBSET_REPACK, nullptr, " Not duplicating %u => %u", 1054 parent_idx, child_idx); 1055 return -1; 1056 } 1057 1058 DEBUG_MSG (SUBSET_REPACK, nullptr, " Duplicating %u => %u", 1059 parent_idx, child_idx); 1060 1061 unsigned clone_idx = duplicate (child_idx); 1062 if (clone_idx == (unsigned) -1) return false; 1063 // duplicate shifts the root node idx, so if parent_idx was root update it. 1064 if (parent_idx == clone_idx) parent_idx++; 1065 1066 auto& parent = vertices_[parent_idx]; 1067 for (auto& l : parent.obj.all_links_writer ()) 1068 { 1069 if (l.objidx != child_idx) 1070 continue; 1071 1072 reassign_link (l, parent_idx, clone_idx); 1073 } 1074 1075 return clone_idx; 1076 } 1077 1078 1079 /* 1080 * Adds a new node to the graph, not connected to anything. 1081 */ new_nodegraph::graph_t1082 unsigned new_node (char* head, char* tail) 1083 { 1084 positions_invalid = true; 1085 distance_invalid = true; 1086 1087 auto* clone = vertices_.push (); 1088 if (vertices_.in_error ()) { 1089 return -1; 1090 } 1091 1092 clone->obj.head = head; 1093 clone->obj.tail = tail; 1094 clone->distance = 0; 1095 clone->space = 0; 1096 1097 unsigned clone_idx = vertices_.length - 2; 1098 1099 // The last object is the root of the graph, so swap back the root to the end. 1100 // The root's obj idx does change, however since it's root nothing else refers to it. 1101 // all other obj idx's will be unaffected. 1102 hb_swap (vertices_[vertices_.length - 2], *clone); 1103 1104 // Since the root moved, update the parents arrays of all children on the root. 1105 for (const auto& l : root ().obj.all_links ()) 1106 vertices_[l.objidx].remap_parent (root_idx () - 1, root_idx ()); 1107 1108 return clone_idx; 1109 } 1110 1111 /* 1112 * Raises the sorting priority of all children. 1113 */ raise_childrens_prioritygraph::graph_t1114 bool raise_childrens_priority (unsigned parent_idx) 1115 { 1116 DEBUG_MSG (SUBSET_REPACK, nullptr, " Raising priority of all children of %u", 1117 parent_idx); 1118 // This operation doesn't change ordering until a sort is run, so no need 1119 // to invalidate positions. It does not change graph structure so no need 1120 // to update distances or edge counts. 1121 auto& parent = vertices_[parent_idx].obj; 1122 bool made_change = false; 1123 for (auto& l : parent.all_links_writer ()) 1124 made_change |= vertices_[l.objidx].raise_priority (); 1125 return made_change; 1126 } 1127 is_fully_connectedgraph::graph_t1128 bool is_fully_connected () 1129 { 1130 update_parents(); 1131 1132 if (root().incoming_edges ()) 1133 // Root cannot have parents. 1134 return false; 1135 1136 for (unsigned i = 0; i < root_idx (); i++) 1137 { 1138 if (!vertices_[i].incoming_edges ()) 1139 return false; 1140 } 1141 return true; 1142 } 1143 1144 #if 0 1145 /* 1146 * Saves the current graph to a packed binary format which the repacker fuzzer takes 1147 * as a seed. 1148 */ 1149 void save_fuzzer_seed (hb_tag_t tag) const 1150 { 1151 FILE* f = fopen ("./repacker_fuzzer_seed", "w"); 1152 fwrite ((void*) &tag, sizeof (tag), 1, f); 1153 1154 uint16_t num_objects = vertices_.length; 1155 fwrite ((void*) &num_objects, sizeof (num_objects), 1, f); 1156 1157 for (const auto& v : vertices_) 1158 { 1159 uint16_t blob_size = v.table_size (); 1160 fwrite ((void*) &blob_size, sizeof (blob_size), 1, f); 1161 fwrite ((const void*) v.obj.head, blob_size, 1, f); 1162 } 1163 1164 uint16_t link_count = 0; 1165 for (const auto& v : vertices_) 1166 link_count += v.obj.real_links.length; 1167 1168 fwrite ((void*) &link_count, sizeof (link_count), 1, f); 1169 1170 typedef struct 1171 { 1172 uint16_t parent; 1173 uint16_t child; 1174 uint16_t position; 1175 uint8_t width; 1176 } link_t; 1177 1178 for (unsigned i = 0; i < vertices_.length; i++) 1179 { 1180 for (const auto& l : vertices_[i].obj.real_links) 1181 { 1182 link_t link { 1183 (uint16_t) i, (uint16_t) l.objidx, 1184 (uint16_t) l.position, (uint8_t) l.width 1185 }; 1186 fwrite ((void*) &link, sizeof (link), 1, f); 1187 } 1188 } 1189 1190 fclose (f); 1191 } 1192 #endif 1193 print_orphaned_nodesgraph::graph_t1194 void print_orphaned_nodes () 1195 { 1196 if (!DEBUG_ENABLED(SUBSET_REPACK)) return; 1197 1198 DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected."); 1199 parents_invalid = true; 1200 update_parents(); 1201 1202 if (root().incoming_edges ()) { 1203 DEBUG_MSG (SUBSET_REPACK, nullptr, "Root node has incoming edges."); 1204 } 1205 1206 for (unsigned i = 0; i < root_idx (); i++) 1207 { 1208 const auto& v = vertices_[i]; 1209 if (!v.incoming_edges ()) 1210 DEBUG_MSG (SUBSET_REPACK, nullptr, "Node %u is orphaned.", i); 1211 } 1212 } 1213 num_roots_for_spacegraph::graph_t1214 unsigned num_roots_for_space (unsigned space) const 1215 { 1216 return num_roots_for_space_[space]; 1217 } 1218 next_spacegraph::graph_t1219 unsigned next_space () const 1220 { 1221 return num_roots_for_space_.length; 1222 } 1223 move_to_new_spacegraph::graph_t1224 void move_to_new_space (const hb_set_t& indices) 1225 { 1226 num_roots_for_space_.push (0); 1227 unsigned new_space = num_roots_for_space_.length - 1; 1228 1229 for (unsigned index : indices) { 1230 auto& node = vertices_[index]; 1231 num_roots_for_space_[node.space] = num_roots_for_space_[node.space] - 1; 1232 num_roots_for_space_[new_space] = num_roots_for_space_[new_space] + 1; 1233 node.space = new_space; 1234 distance_invalid = true; 1235 positions_invalid = true; 1236 } 1237 } 1238 space_forgraph::graph_t1239 unsigned space_for (unsigned index, unsigned* root = nullptr) const 1240 { 1241 loop: 1242 assert (index < vertices_.length); 1243 const auto& node = vertices_[index]; 1244 if (node.space) 1245 { 1246 if (root != nullptr) 1247 *root = index; 1248 return node.space; 1249 } 1250 1251 if (!node.incoming_edges ()) 1252 { 1253 if (root) 1254 *root = index; 1255 return 0; 1256 } 1257 1258 index = *node.parents_iter (); 1259 goto loop; 1260 } 1261 err_other_errorgraph::graph_t1262 void err_other_error () { this->successful = false; } 1263 total_size_in_bytesgraph::graph_t1264 size_t total_size_in_bytes () const { 1265 size_t total_size = 0; 1266 unsigned count = vertices_.length; 1267 for (unsigned i = 0; i < count; i++) { 1268 size_t size = vertices_.arrayZ[i].obj.tail - vertices_.arrayZ[i].obj.head; 1269 total_size += size; 1270 } 1271 return total_size; 1272 } 1273 1274 1275 private: 1276 1277 /* 1278 * Returns the numbers of incoming edges that are 24 or 32 bits wide. 1279 */ wide_parentsgraph::graph_t1280 unsigned wide_parents (unsigned node_idx, hb_set_t& parents) const 1281 { 1282 unsigned count = 0; 1283 for (unsigned p : vertices_[node_idx].parents_iter ()) 1284 { 1285 // Only real links can be wide 1286 for (const auto& l : vertices_[p].obj.real_links) 1287 { 1288 if (l.objidx == node_idx 1289 && (l.width == 3 || l.width == 4) 1290 && !l.is_signed) 1291 { 1292 count++; 1293 parents.add (p); 1294 } 1295 } 1296 } 1297 return count; 1298 } 1299 check_successgraph::graph_t1300 bool check_success (bool success) 1301 { return this->successful && (success || ((void) err_other_error (), false)); } 1302 1303 public: 1304 /* 1305 * Creates a map from objid to # of incoming edges. 1306 */ update_parentsgraph::graph_t1307 void update_parents () 1308 { 1309 if (!parents_invalid) return; 1310 1311 unsigned count = vertices_.length; 1312 1313 for (unsigned i = 0; i < count; i++) 1314 vertices_.arrayZ[i].reset_parents (); 1315 1316 for (unsigned p = 0; p < count; p++) 1317 { 1318 for (auto& l : vertices_.arrayZ[p].obj.all_links ()) 1319 vertices_[l.objidx].add_parent (p); 1320 } 1321 1322 for (unsigned i = 0; i < count; i++) 1323 // parents arrays must be accurate or downstream operations like cycle detection 1324 // and sorting won't work correctly. 1325 check_success (!vertices_.arrayZ[i].in_error ()); 1326 1327 parents_invalid = false; 1328 } 1329 1330 /* 1331 * compute the serialized start and end positions for each vertex. 1332 */ update_positionsgraph::graph_t1333 void update_positions () 1334 { 1335 if (!positions_invalid) return; 1336 1337 unsigned current_pos = 0; 1338 for (int i = root_idx (); i >= 0; i--) 1339 { 1340 auto& v = vertices_[i]; 1341 v.start = current_pos; 1342 current_pos += v.obj.tail - v.obj.head; 1343 v.end = current_pos; 1344 } 1345 1346 positions_invalid = false; 1347 } 1348 1349 /* 1350 * Finds the distance to each object in the graph 1351 * from the initial node. 1352 */ update_distancesgraph::graph_t1353 void update_distances () 1354 { 1355 if (!distance_invalid) return; 1356 1357 // Uses Dijkstra's algorithm to find all of the shortest distances. 1358 // https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm 1359 // 1360 // Implementation Note: 1361 // Since our priority queue doesn't support fast priority decreases 1362 // we instead just add new entries into the queue when a priority changes. 1363 // Redundant ones are filtered out later on by the visited set. 1364 // According to https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf 1365 // for practical performance this is faster then using a more advanced queue 1366 // (such as a fibonacci queue) with a fast decrease priority. 1367 unsigned count = vertices_.length; 1368 for (unsigned i = 0; i < count; i++) 1369 vertices_.arrayZ[i].distance = hb_int_max (int64_t); 1370 vertices_.tail ().distance = 0; 1371 1372 hb_priority_queue_t<int64_t> queue; 1373 queue.insert (0, vertices_.length - 1); 1374 1375 hb_vector_t<bool> visited; 1376 visited.resize (vertices_.length); 1377 1378 while (!queue.in_error () && !queue.is_empty ()) 1379 { 1380 unsigned next_idx = queue.pop_minimum ().second; 1381 if (visited[next_idx]) continue; 1382 const auto& next = vertices_[next_idx]; 1383 int64_t next_distance = vertices_[next_idx].distance; 1384 visited[next_idx] = true; 1385 1386 for (const auto& link : next.obj.all_links ()) 1387 { 1388 if (visited[link.objidx]) continue; 1389 1390 const auto& child = vertices_.arrayZ[link.objidx].obj; 1391 unsigned link_width = link.width ? link.width : 4; // treat virtual offsets as 32 bits wide 1392 int64_t child_weight = (child.tail - child.head) + 1393 ((int64_t) 1 << (link_width * 8)) * (vertices_.arrayZ[link.objidx].space + 1); 1394 int64_t child_distance = next_distance + child_weight; 1395 1396 if (child_distance < vertices_.arrayZ[link.objidx].distance) 1397 { 1398 vertices_.arrayZ[link.objidx].distance = child_distance; 1399 queue.insert (child_distance, link.objidx); 1400 } 1401 } 1402 } 1403 1404 check_success (!queue.in_error ()); 1405 if (!check_success (queue.is_empty ())) 1406 { 1407 print_orphaned_nodes (); 1408 return; 1409 } 1410 1411 distance_invalid = false; 1412 } 1413 1414 private: 1415 /* 1416 * Updates a link in the graph to point to a different object. Corrects the 1417 * parents vector on the previous and new child nodes. 1418 */ reassign_linkgraph::graph_t1419 void reassign_link (hb_serialize_context_t::object_t::link_t& link, 1420 unsigned parent_idx, 1421 unsigned new_idx) 1422 { 1423 unsigned old_idx = link.objidx; 1424 link.objidx = new_idx; 1425 vertices_[old_idx].remove_parent (parent_idx); 1426 vertices_[new_idx].add_parent (parent_idx); 1427 } 1428 1429 /* 1430 * Updates all objidx's in all links using the provided mapping. Corrects incoming edge counts. 1431 */ 1432 template<typename Iterator, hb_requires (hb_is_iterator (Iterator))> remap_obj_indicesgraph::graph_t1433 void remap_obj_indices (const hb_map_t& id_map, 1434 Iterator subgraph, 1435 bool only_wide = false) 1436 { 1437 if (!id_map) return; 1438 for (unsigned i : subgraph) 1439 { 1440 for (auto& link : vertices_[i].obj.all_links_writer ()) 1441 { 1442 const uint32_t *v; 1443 if (!id_map.has (link.objidx, &v)) continue; 1444 if (only_wide && !(link.width == 4 && !link.is_signed)) continue; 1445 1446 reassign_link (link, i, *v); 1447 } 1448 } 1449 } 1450 1451 /* 1452 * Updates all objidx's in all links using the provided mapping. 1453 */ remap_all_obj_indicesgraph::graph_t1454 bool remap_all_obj_indices (const hb_vector_t<unsigned>& id_map, 1455 hb_vector_t<vertex_t>* sorted_graph) const 1456 { 1457 unsigned count = sorted_graph->length; 1458 for (unsigned i = 0; i < count; i++) 1459 { 1460 if (!(*sorted_graph)[i].remap_parents (id_map)) 1461 return false; 1462 for (auto& link : sorted_graph->arrayZ[i].obj.all_links_writer ()) 1463 { 1464 link.objidx = id_map[link.objidx]; 1465 } 1466 } 1467 return true; 1468 } 1469 1470 /* 1471 * Finds all nodes in targets that are reachable from start_idx, nodes in visited will be skipped. 1472 * For this search the graph is treated as being undirected. 1473 * 1474 * Connected targets will be added to connected and removed from targets. All visited nodes 1475 * will be added to visited. 1476 */ find_connected_nodesgraph::graph_t1477 void find_connected_nodes (unsigned start_idx, 1478 hb_set_t& targets, 1479 hb_set_t& visited, 1480 hb_set_t& connected) 1481 { 1482 if (unlikely (!check_success (!visited.in_error ()))) return; 1483 if (visited.has (start_idx)) return; 1484 visited.add (start_idx); 1485 1486 if (targets.has (start_idx)) 1487 { 1488 targets.del (start_idx); 1489 connected.add (start_idx); 1490 } 1491 1492 const auto& v = vertices_[start_idx]; 1493 1494 // Graph is treated as undirected so search children and parents of start_idx 1495 for (const auto& l : v.obj.all_links ()) 1496 find_connected_nodes (l.objidx, targets, visited, connected); 1497 1498 for (unsigned p : v.parents_iter ()) 1499 find_connected_nodes (p, targets, visited, connected); 1500 } 1501 1502 public: 1503 // TODO(garretrieger): make private, will need to move most of offset overflow code into graph. 1504 hb_vector_t<vertex_t> vertices_; 1505 hb_vector_t<vertex_t> vertices_scratch_; 1506 private: 1507 bool parents_invalid; 1508 bool distance_invalid; 1509 bool positions_invalid; 1510 bool successful; 1511 hb_vector_t<unsigned> num_roots_for_space_; 1512 hb_vector_t<char*> buffers; 1513 }; 1514 1515 } 1516 1517 #endif // GRAPH_GRAPH_HH 1518