1 /*
2  * Copyright © 2020  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Garret Rieger
25  */
26 
27 #ifndef HB_REPACKER_HH
28 #define HB_REPACKER_HH
29 
30 #include "hb-open-type.hh"
31 #include "hb-map.hh"
32 #include "hb-priority-queue.hh"
33 #include "hb-serialize.hh"
34 #include "hb-vector.hh"
35 
36 
37 struct graph_t
38 {
39   struct vertex_t
40   {
vertex_tgraph_t::vertex_t41     vertex_t () :
42         distance (0),
43         incoming_edges (0),
44         start (0),
45         end (0),
46         priority(0) {}
47 
finigraph_t::vertex_t48     void fini () { obj.fini (); }
49 
50     hb_serialize_context_t::object_t obj;
51     int64_t distance;
52     unsigned incoming_edges;
53     unsigned start;
54     unsigned end;
55     unsigned priority;
56 
is_sharedgraph_t::vertex_t57     bool is_shared () const
58     {
59       return incoming_edges > 1;
60     }
61 
is_leafgraph_t::vertex_t62     bool is_leaf () const
63     {
64       return !obj.links.length;
65     }
66 
raise_prioritygraph_t::vertex_t67     void raise_priority ()
68     {
69       priority++;
70     }
71 
modified_distancegraph_t::vertex_t72     int64_t modified_distance (unsigned order) const
73     {
74       // TODO(garretrieger): once priority is high enough, should try
75       // setting distance = 0 which will force to sort immediately after
76       // it's parent where possible.
77 
78       int64_t modified_distance =
79           hb_min (hb_max(distance + distance_modifier (), 0), 0x7FFFFFFFFF);
80       return (modified_distance << 24) | (0x00FFFFFF & order);
81     }
82 
distance_modifiergraph_t::vertex_t83     int64_t distance_modifier () const
84     {
85       if (!priority) return 0;
86       int64_t table_size = obj.tail - obj.head;
87       return -(table_size - table_size / (1 << hb_min(priority, 16u)));
88     }
89   };
90 
91   struct overflow_record_t
92   {
93     unsigned parent;
94     const hb_serialize_context_t::object_t::link_t* link;
95   };
96 
97   struct clone_buffer_t
98   {
clone_buffer_tgraph_t::clone_buffer_t99     clone_buffer_t () : head (nullptr), tail (nullptr) {}
100 
copygraph_t::clone_buffer_t101     bool copy (const hb_serialize_context_t::object_t& object)
102     {
103       fini ();
104       unsigned size = object.tail - object.head;
105       head = (char*) malloc (size);
106       if (!head) return false;
107 
108       memcpy (head, object.head, size);
109       tail = head + size;
110       return true;
111     }
112 
113     char* head;
114     char* tail;
115 
finigraph_t::clone_buffer_t116     void fini ()
117     {
118       if (!head) return;
119       free (head);
120       head = nullptr;
121     }
122   };
123 
124   /*
125    * A topological sorting of an object graph. Ordered
126    * in reverse serialization order (first object in the
127    * serialization is at the end of the list). This matches
128    * the 'packed' object stack used internally in the
129    * serializer
130    */
graph_tgraph_t131   graph_t (const hb_vector_t<hb_serialize_context_t::object_t *>& objects)
132       : edge_count_invalid (true),
133         distance_invalid (true),
134         positions_invalid (true),
135         successful (true)
136   {
137     bool removed_nil = false;
138     for (unsigned i = 0; i < objects.length; i++)
139     {
140       // TODO(grieger): check all links point to valid objects.
141 
142       // If this graph came from a serialization buffer object 0 is the
143       // nil object. We don't need it for our purposes here so drop it.
144       if (i == 0 && !objects[i])
145       {
146         removed_nil = true;
147         continue;
148       }
149 
150       vertex_t* v = vertices_.push ();
151       if (check_success (!vertices_.in_error ()))
152         v->obj = *objects[i];
153       if (!removed_nil) continue;
154       for (unsigned i = 0; i < v->obj.links.length; i++)
155         // Fix indices to account for removed nil object.
156         v->obj.links[i].objidx--;
157     }
158   }
159 
~graph_tgraph_t160   ~graph_t ()
161   {
162     vertices_.fini_deep ();
163     clone_buffers_.fini_deep ();
164   }
165 
in_errorgraph_t166   bool in_error () const
167   {
168     return !successful || vertices_.in_error () || clone_buffers_.in_error ();
169   }
170 
rootgraph_t171   const vertex_t& root () const
172   {
173     return vertices_[root_idx ()];
174   }
175 
root_idxgraph_t176   unsigned root_idx () const
177   {
178     // Object graphs are in reverse order, the first object is at the end
179     // of the vector. Since the graph is topologically sorted it's safe to
180     // assume the first object has no incoming edges.
181     return vertices_.length - 1;
182   }
183 
objectgraph_t184   const hb_serialize_context_t::object_t& object(unsigned i) const
185   {
186     return vertices_[i].obj;
187   }
188 
189   /*
190    * serialize graph into the provided serialization buffer.
191    */
serializegraph_t192   void serialize (hb_serialize_context_t* c) const
193   {
194     c->start_serialize<void> ();
195     for (unsigned i = 0; i < vertices_.length; i++) {
196       c->push ();
197 
198       size_t size = vertices_[i].obj.tail - vertices_[i].obj.head;
199       char* start = c->allocate_size <char> (size);
200       if (!start) return;
201 
202       memcpy (start, vertices_[i].obj.head, size);
203 
204       for (const auto& link : vertices_[i].obj.links)
205         serialize_link (link, start, c);
206 
207       // All duplications are already encoded in the graph, so don't
208       // enable sharing during packing.
209       c->pop_pack (false);
210     }
211     c->end_serialize ();
212   }
213 
214   /*
215    * Generates a new topological sorting of graph using Kahn's
216    * algorithm: https://en.wikipedia.org/wiki/Topological_sorting#Algorithms
217    */
sort_kahngraph_t218   void sort_kahn ()
219   {
220     positions_invalid = true;
221 
222     if (vertices_.length <= 1) {
223       // Graph of 1 or less doesn't need sorting.
224       return;
225     }
226 
227     hb_vector_t<unsigned> queue;
228     hb_vector_t<vertex_t> sorted_graph;
229     hb_vector_t<unsigned> id_map;
230     if (unlikely (!check_success (id_map.resize (vertices_.length)))) return;
231 
232     hb_vector_t<unsigned> removed_edges;
233     if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return;
234     update_incoming_edge_count ();
235 
236     queue.push (root_idx ());
237     int new_id = vertices_.length - 1;
238 
239     while (!queue.in_error () && queue.length)
240     {
241       unsigned next_id = queue[0];
242       queue.remove (0);
243 
244       vertex_t& next = vertices_[next_id];
245       sorted_graph.push (next);
246       id_map[next_id] = new_id--;
247 
248       for (const auto& link : next.obj.links) {
249         removed_edges[link.objidx]++;
250         if (!(vertices_[link.objidx].incoming_edges - removed_edges[link.objidx]))
251           queue.push (link.objidx);
252       }
253     }
254 
255     check_success (!queue.in_error ());
256     check_success (!sorted_graph.in_error ());
257     if (!check_success (new_id == -1))
258       DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
259 
260     remap_obj_indices (id_map, &sorted_graph);
261 
262     sorted_graph.as_array ().reverse ();
263 
264     vertices_.fini_deep ();
265     vertices_ = sorted_graph;
266     sorted_graph.fini_deep ();
267   }
268 
269   /*
270    * Generates a new topological sorting of graph ordered by the shortest
271    * distance to each node.
272    */
sort_shortest_distancegraph_t273   void sort_shortest_distance ()
274   {
275     positions_invalid = true;
276 
277     if (vertices_.length <= 1) {
278       // Graph of 1 or less doesn't need sorting.
279       return;
280     }
281 
282     update_distances ();
283 
284     hb_priority_queue_t queue;
285     hb_vector_t<vertex_t> sorted_graph;
286     hb_vector_t<unsigned> id_map;
287     if (unlikely (!check_success (id_map.resize (vertices_.length)))) return;
288 
289     hb_vector_t<unsigned> removed_edges;
290     if (unlikely (!check_success (removed_edges.resize (vertices_.length)))) return;
291     update_incoming_edge_count ();
292 
293     queue.insert (root ().modified_distance (0), root_idx ());
294     int new_id = root_idx ();
295     unsigned order = 1;
296     while (!queue.in_error () && !queue.is_empty ())
297     {
298       unsigned next_id = queue.pop_minimum().second;
299 
300       vertex_t& next = vertices_[next_id];
301       sorted_graph.push (next);
302       id_map[next_id] = new_id--;
303 
304       for (const auto& link : next.obj.links) {
305         removed_edges[link.objidx]++;
306         if (!(vertices_[link.objidx].incoming_edges - removed_edges[link.objidx]))
307           // Add the order that the links were encountered to the priority.
308           // This ensures that ties between priorities objects are broken in a consistent
309           // way. More specifically this is set up so that if a set of objects have the same
310           // distance they'll be added to the topological order in the order that they are
311           // referenced from the parent object.
312           queue.insert (vertices_[link.objidx].modified_distance (order++),
313                         link.objidx);
314       }
315     }
316 
317     check_success (!queue.in_error ());
318     check_success (!sorted_graph.in_error ());
319     if (!check_success (new_id == -1))
320       DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
321 
322     remap_obj_indices (id_map, &sorted_graph);
323 
324     sorted_graph.as_array ().reverse ();
325 
326     vertices_.fini_deep ();
327     vertices_ = sorted_graph;
328     sorted_graph.fini_deep ();
329   }
330 
331   /*
332    * Creates a copy of child and re-assigns the link from
333    * parent to the clone. The copy is a shallow copy, objects
334    * linked from child are not duplicated.
335    */
duplicategraph_t336   void duplicate (unsigned parent_idx, unsigned child_idx)
337   {
338     DEBUG_MSG (SUBSET_REPACK, nullptr, "  Duplicating %d => %d",
339                parent_idx, child_idx);
340 
341     positions_invalid = true;
342 
343     auto* clone = vertices_.push ();
344     auto& child = vertices_[child_idx];
345     clone_buffer_t* buffer = clone_buffers_.push ();
346     if (vertices_.in_error ()
347         || clone_buffers_.in_error ()
348         || !check_success (buffer->copy (child.obj))) {
349       return;
350     }
351 
352     clone->obj.head = buffer->head;
353     clone->obj.tail = buffer->tail;
354     clone->distance = child.distance;
355 
356     for (const auto& l : child.obj.links)
357       clone->obj.links.push (l);
358 
359     check_success (!clone->obj.links.in_error ());
360 
361     auto& parent = vertices_[parent_idx];
362     unsigned clone_idx = vertices_.length - 2;
363     for (unsigned i = 0; i < parent.obj.links.length; i++)
364     {
365       auto& l = parent.obj.links[i];
366       if (l.objidx == child_idx)
367       {
368         l.objidx = clone_idx;
369         clone->incoming_edges++;
370         child.incoming_edges--;
371       }
372     }
373 
374     // The last object is the root of the graph, so swap back the root to the end.
375     // The root's obj idx does change, however since it's root nothing else refers to it.
376     // all other obj idx's will be unaffected.
377     vertex_t root = vertices_[vertices_.length - 2];
378     vertices_[vertices_.length - 2] = *clone;
379     vertices_[vertices_.length - 1] = root;
380   }
381 
382   /*
383    * Raises the sorting priority of all children.
384    */
raise_childrens_prioritygraph_t385   void raise_childrens_priority (unsigned parent_idx)
386   {
387     DEBUG_MSG (SUBSET_REPACK, nullptr, "  Raising priority of all children of %d",
388                parent_idx);
389     // This operation doesn't change ordering until a sort is run, so no need
390     // to invalidate positions. It does not change graph structure so no need
391     // to update distances or edge counts.
392     auto& parent = vertices_[parent_idx].obj;
393     for (unsigned i = 0; i < parent.links.length; i++)
394       vertices_[parent.links[i].objidx].raise_priority ();
395   }
396 
397   /*
398    * Will any offsets overflow on graph when it's serialized?
399    */
will_overflowgraph_t400   bool will_overflow (hb_vector_t<overflow_record_t>* overflows = nullptr)
401   {
402     if (overflows) overflows->resize (0);
403     update_positions ();
404 
405     for (int parent_idx = vertices_.length - 1; parent_idx >= 0; parent_idx--)
406     {
407       for (const auto& link : vertices_[parent_idx].obj.links)
408       {
409         int64_t offset = compute_offset (parent_idx, link);
410         if (is_valid_offset (offset, link))
411           continue;
412 
413         if (!overflows) return true;
414 
415         overflow_record_t r;
416         r.parent = parent_idx;
417         r.link = &link;
418         overflows->push (r);
419       }
420     }
421 
422     if (!overflows) return false;
423     return overflows->length;
424   }
425 
print_overflowsgraph_t426   void print_overflows (const hb_vector_t<overflow_record_t>& overflows)
427   {
428     if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
429 
430     update_incoming_edge_count ();
431     for (const auto& o : overflows)
432     {
433       const auto& child = vertices_[o.link->objidx];
434       DEBUG_MSG (SUBSET_REPACK, nullptr, "  overflow from %d => %d (%d incoming , %d outgoing)",
435                  o.parent,
436                  o.link->objidx,
437                  child.incoming_edges,
438                  child.obj.links.length);
439     }
440   }
441 
err_other_errorgraph_t442   void err_other_error () { this->successful = false; }
443 
444  private:
445 
check_successgraph_t446   bool check_success (bool success)
447   { return this->successful && (success || (err_other_error (), false)); }
448 
449   /*
450    * Creates a map from objid to # of incoming edges.
451    */
update_incoming_edge_countgraph_t452   void update_incoming_edge_count ()
453   {
454     if (!edge_count_invalid) return;
455 
456     for (unsigned i = 0; i < vertices_.length; i++)
457       vertices_[i].incoming_edges = 0;
458 
459     for (const vertex_t& v : vertices_)
460     {
461       for (auto& l : v.obj.links)
462       {
463         vertices_[l.objidx].incoming_edges++;
464       }
465     }
466 
467     edge_count_invalid = false;
468   }
469 
470   /*
471    * compute the serialized start and end positions for each vertex.
472    */
update_positionsgraph_t473   void update_positions ()
474   {
475     if (!positions_invalid) return;
476 
477     unsigned current_pos = 0;
478     for (int i = root_idx (); i >= 0; i--)
479     {
480       auto& v = vertices_[i];
481       v.start = current_pos;
482       current_pos += v.obj.tail - v.obj.head;
483       v.end = current_pos;
484     }
485 
486     positions_invalid = false;
487   }
488 
489   /*
490    * Finds the distance to each object in the graph
491    * from the initial node.
492    */
update_distancesgraph_t493   void update_distances ()
494   {
495     if (!distance_invalid) return;
496 
497     // Uses Dijkstra's algorithm to find all of the shortest distances.
498     // https://en.wikipedia.org/wiki/Dijkstra%27s_algorithm
499     //
500     // Implementation Note:
501     // Since our priority queue doesn't support fast priority decreases
502     // we instead just add new entries into the queue when a priority changes.
503     // Redundant ones are filtered out later on by the visited set.
504     // According to https://www3.cs.stonybrook.edu/~rezaul/papers/TR-07-54.pdf
505     // for practical performance this is faster then using a more advanced queue
506     // (such as a fibonaacci queue) with a fast decrease priority.
507     for (unsigned i = 0; i < vertices_.length; i++)
508     {
509       if (i == vertices_.length - 1)
510         vertices_[i].distance = 0;
511       else
512         vertices_[i].distance = hb_int_max (int64_t);
513     }
514 
515     hb_priority_queue_t queue;
516     queue.insert (0, vertices_.length - 1);
517 
518     hb_set_t visited;
519 
520     while (!queue.in_error () && !queue.is_empty ())
521     {
522       unsigned next_idx = queue.pop_minimum ().second;
523       if (visited.has (next_idx)) continue;
524       const auto& next = vertices_[next_idx];
525       int64_t next_distance = vertices_[next_idx].distance;
526       visited.add (next_idx);
527 
528       for (const auto& link : next.obj.links)
529       {
530         if (visited.has (link.objidx)) continue;
531 
532         const auto& child = vertices_[link.objidx].obj;
533         int64_t child_weight = child.tail - child.head +
534                                (!link.is_wide ? (1 << 16) : ((int64_t) 1 << 32));
535         int64_t child_distance = next_distance + child_weight;
536 
537         if (child_distance < vertices_[link.objidx].distance)
538         {
539           vertices_[link.objidx].distance = child_distance;
540           queue.insert (child_distance, link.objidx);
541         }
542       }
543     }
544 
545     check_success (!queue.in_error ());
546     if (!check_success (queue.is_empty ()))
547     {
548       DEBUG_MSG (SUBSET_REPACK, nullptr, "Graph is not fully connected.");
549       return;
550     }
551 
552     distance_invalid = false;
553   }
554 
compute_offsetgraph_t555   int64_t compute_offset (
556       unsigned parent_idx,
557       const hb_serialize_context_t::object_t::link_t& link) const
558   {
559     const auto& parent = vertices_[parent_idx];
560     const auto& child = vertices_[link.objidx];
561     int64_t offset = 0;
562     switch ((hb_serialize_context_t::whence_t) link.whence) {
563       case hb_serialize_context_t::whence_t::Head:
564         offset = child.start - parent.start; break;
565       case hb_serialize_context_t::whence_t::Tail:
566         offset = child.start - parent.end; break;
567       case hb_serialize_context_t::whence_t::Absolute:
568         offset = child.start; break;
569     }
570 
571     assert (offset >= link.bias);
572     offset -= link.bias;
573     return offset;
574   }
575 
is_valid_offsetgraph_t576   bool is_valid_offset (int64_t offset,
577                         const hb_serialize_context_t::object_t::link_t& link) const
578   {
579     if (link.is_signed)
580     {
581       if (link.is_wide)
582         return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31);
583       else
584         return offset >= -(1 << 15) && offset < (1 << 15);
585     }
586     else
587     {
588       if (link.is_wide)
589         return offset >= 0 && offset < ((int64_t) 1 << 32);
590       else
591         return offset >= 0 && offset < (1 << 16);
592     }
593   }
594 
595   /*
596    * Updates all objidx's in all links using the provided mapping.
597    */
remap_obj_indicesgraph_t598   void remap_obj_indices (const hb_vector_t<unsigned>& id_map,
599                           hb_vector_t<vertex_t>* sorted_graph) const
600   {
601     for (unsigned i = 0; i < sorted_graph->length; i++)
602     {
603       for (unsigned j = 0; j < (*sorted_graph)[i].obj.links.length; j++)
604       {
605         auto& link = (*sorted_graph)[i].obj.links[j];
606         link.objidx = id_map[link.objidx];
607       }
608     }
609   }
610 
611   template <typename O> void
serialize_link_of_typegraph_t612   serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
613                           char* head,
614                           hb_serialize_context_t* c) const
615   {
616     OT::Offset<O>* offset = reinterpret_cast<OT::Offset<O>*> (head + link.position);
617     *offset = 0;
618     c->add_link (*offset,
619                  // serializer has an extra nil object at the start of the
620                  // object array. So all id's are +1 of what our id's are.
621                  link.objidx + 1,
622                  (hb_serialize_context_t::whence_t) link.whence,
623                  link.bias);
624   }
625 
serialize_linkgraph_t626   void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
627                  char* head,
628                  hb_serialize_context_t* c) const
629   {
630     if (link.is_wide)
631     {
632       if (link.is_signed)
633       {
634         serialize_link_of_type<OT::HBINT32> (link, head, c);
635       } else {
636         serialize_link_of_type<OT::HBUINT32> (link, head, c);
637       }
638     } else {
639       if (link.is_signed)
640       {
641         serialize_link_of_type<OT::HBINT16> (link, head, c);
642       } else {
643         serialize_link_of_type<OT::HBUINT16> (link, head, c);
644       }
645     }
646   }
647 
648  public:
649   // TODO(garretrieger): make private, will need to move most of offset overflow code into graph.
650   hb_vector_t<vertex_t> vertices_;
651  private:
652   hb_vector_t<clone_buffer_t> clone_buffers_;
653   bool edge_count_invalid;
654   bool distance_invalid;
655   bool positions_invalid;
656   bool successful;
657 };
658 
659 
660 /*
661  * Attempts to modify the topological sorting of the provided object graph to
662  * eliminate offset overflows in the links between objects of the graph. If a
663  * non-overflowing ordering is found the updated graph is serialized it into the
664  * provided serialization context.
665  *
666  * If necessary the structure of the graph may be modified in ways that do not
667  * affect the functionality of the graph. For example shared objects may be
668  * duplicated.
669  */
670 inline void
hb_resolve_overflows(const hb_vector_t<hb_serialize_context_t::object_t * > & packed,hb_serialize_context_t * c)671 hb_resolve_overflows (const hb_vector_t<hb_serialize_context_t::object_t *>& packed,
672                       hb_serialize_context_t* c) {
673   // Kahn sort is ~twice as fast as shortest distance sort and works for many fonts
674   // so try it first to save time.
675   graph_t sorted_graph (packed);
676   sorted_graph.sort_kahn ();
677   if (!sorted_graph.will_overflow ())
678   {
679     sorted_graph.serialize (c);
680     return;
681   }
682 
683   sorted_graph.sort_shortest_distance ();
684 
685   unsigned round = 0;
686   hb_vector_t<graph_t::overflow_record_t> overflows;
687   // TODO(garretrieger): select a good limit for max rounds.
688   while (!sorted_graph.in_error ()
689          && sorted_graph.will_overflow (&overflows)
690          && round++ < 10) {
691     DEBUG_MSG (SUBSET_REPACK, nullptr, "=== Over flow resolution round %d ===", round);
692     sorted_graph.print_overflows (overflows);
693 
694     bool resolution_attempted = false;
695     hb_set_t priority_bumped_parents;
696     // Try resolving the furthest overflows first.
697     for (int i = overflows.length - 1; i >= 0; i--)
698     {
699       const graph_t::overflow_record_t& r = overflows[i];
700       const auto& child = sorted_graph.vertices_[r.link->objidx];
701       if (child.is_shared ())
702       {
703         // The child object is shared, we may be able to eliminate the overflow
704         // by duplicating it.
705         sorted_graph.duplicate (r.parent, r.link->objidx);
706         resolution_attempted = true;
707 
708         // Stop processing overflows for this round so that object order can be
709         // updated to account for the newly added object.
710         break;
711       }
712 
713       if (child.is_leaf () && !priority_bumped_parents.has (r.parent))
714       {
715         // This object is too far from it's parent, attempt to move it closer.
716         //
717         // TODO(garretrieger): initially limiting this to leaf's since they can be
718         //                     moved closer with fewer consequences. However, this can
719         //                     likely can be used for non-leafs as well.
720         // TODO(garretrieger): add a maximum priority, don't try to raise past this.
721         // TODO(garretrieger): also try lowering priority of the parent. Make it
722         //                     get placed further up in the ordering, closer to it's children.
723         //                     this is probably preferable if the total size of the parent object
724         //                     is < then the total size of the children (and the parent can be moved).
725         //                     Since in that case moving the parent will cause a smaller increase in
726         //                     the length of other offsets.
727         sorted_graph.raise_childrens_priority (r.parent);
728         priority_bumped_parents.add (r.parent);
729         resolution_attempted = true;
730         continue;
731       }
732 
733       // TODO(garretrieger): add additional offset resolution strategies
734       // - Promotion to extension lookups.
735       // - Table splitting.
736     }
737 
738     if (resolution_attempted)
739     {
740       sorted_graph.sort_shortest_distance ();
741       continue;
742     }
743 
744     DEBUG_MSG (SUBSET_REPACK, nullptr, "No resolution available :(");
745     c->err (HB_SERIALIZE_ERROR_OFFSET_OVERFLOW);
746     return;
747   }
748 
749   if (sorted_graph.in_error ())
750   {
751     c->err (HB_SERIALIZE_ERROR_OTHER);
752     return;
753   }
754   sorted_graph.serialize (c);
755 }
756 
757 
758 #endif /* HB_REPACKER_HH */
759