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