tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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