[subset] Start a proof of concept implementation of the GSUB/GPOS offset overflow resolver.
diff --git a/src/hb-repacker.hh b/src/hb-repacker.hh new file mode 100644 index 0000000..4bcc239 --- /dev/null +++ b/src/hb-repacker.hh
@@ -0,0 +1,180 @@ +/* + * Copyright © 2020 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Google Author(s): Garret Rieger + */ + +#ifndef HB_REPACKER_HH +#define HB_REPACKER_HH + +#include "hb-open-type.hh" +#include "hb-serialize.hh" +#include "hb-vector.hh" + + +struct graph_t +{ + /* + * A topological sorting of an object graph. Ordered + * in reverse serialization order (first object in the + * serialization is at the end of the graph). This matches + * the 'packed' object stack used internally in the + * serializer + */ + graph_t (const hb_vector_t<hb_serialize_context_t::object_t *>& objects) + : objects_ (objects) + {} + + /* + * serialize graph into the provided serialization buffer. + */ + void serialize (hb_serialize_context_t* c) + { + c->start_serialize<void> (); + for (unsigned i = 0; i < objects_.length; i++) { + if (!objects_[i]) continue; + + c->push (); + + size_t size = objects_[i]->tail - objects_[i]->head; + char* start = c->allocate_size <char> (size); + if (!start) return; + + memcpy (start, objects_[i]->head, size); + + for (const auto& link : objects_[i]->links) + serialize_link (link, start, c); + + c->pop_pack (false); + } + c->end_serialize (); + } + + /* + * Generates a new topological sorting of graph using BFS. + */ + void sort_bfs () + { + hb_vector_t<int> queue; + hb_vector_t<hb_serialize_context_t::object_t *> sorted_graph; + + // Object graphs are in reverse order, the first object is at the end + // of the vector. + queue.push (objects_.length - 1); + + hb_set_t visited; + while (queue.length) + { + int next_id = queue[0]; + queue.remove(0); + visited.add(next_id); + + hb_serialize_context_t::object_t* next = objects_[next_id]; + sorted_graph.push (next); + + for (const auto& link : next->links) { + if (!visited.has (link.objidx)) + queue.push (link.objidx); + } + } + + sorted_graph.as_array ().reverse (); + objects_ = sorted_graph; + // TODO(garretrieger): remap object id's on the links. + // TODO(garretrieger): what order should graphs be in (first object at the end? or the beginning) + + // TODO(garretrieger): check that all objects made it over into the sorted copy + // (ie. all objects are connected in the original graph). + } + + /* + * Will any offsets overflow on graph when it's serialized? + */ + bool will_overflow() + { + // TODO(garretrieger): implement me. + return false; + } + + private: + + template <typename O> void + serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link, + char* head, + hb_serialize_context_t* c) + { + OT::Offset<O>* offset = reinterpret_cast<OT::Offset<O>*> (head + link.position); + *offset = 0; + c->add_link (*offset, + link.objidx, + (hb_serialize_context_t::whence_t) link.whence, + link.bias); + } + + void serialize_link (const hb_serialize_context_t::object_t::link_t& link, + char* head, + hb_serialize_context_t* c) + { + if (link.is_wide) + { + if (link.is_signed) + { + serialize_link_of_type<OT::HBINT32> (link, head, c); + } else { + serialize_link_of_type<OT::HBUINT32> (link, head, c); + } + } else { + if (link.is_signed) + { + serialize_link_of_type<OT::HBINT16> (link, head, c); + } else { + serialize_link_of_type<OT::HBUINT16> (link, head, c); + } + } + } + + hb_vector_t<hb_serialize_context_t::object_t *> objects_; +}; + + +/* + * Re-serialize the provided object graph into the serialization context + * using BFS (Breadth First Search) to produce the topological ordering. + */ +inline void +hb_resolve_overflows (const hb_vector_t<hb_serialize_context_t::object_t *>& packed, + hb_serialize_context_t* c) { + graph_t sorted_graph (packed); + sorted_graph.sort_bfs (); + if (sorted_graph.will_overflow ()) { + // TODO(garretrieger): additional offset resolution strategies + // - Promotion to extension lookups. + // - Table duplication. + // - Table splitting. + } + + sorted_graph.serialize (c); +} + + +#endif /* HB_REPACKER_HH */