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