[repacker] add sanitization for GSUB/LookupList/Lookup during extension promotion.
This commit is contained in:
parent
c61c05e431
commit
ede7b74584
|
@ -621,7 +621,6 @@ struct graph_t
|
||||||
clone->obj.tail = tail;
|
clone->obj.tail = tail;
|
||||||
clone->distance = 0;
|
clone->distance = 0;
|
||||||
clone->space = 0;
|
clone->space = 0;
|
||||||
printf("Creating new node from %p to %p\n", head, tail);
|
|
||||||
|
|
||||||
unsigned clone_idx = vertices_.length - 2;
|
unsigned clone_idx = vertices_.length - 2;
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,22 @@
|
||||||
|
|
||||||
namespace graph {
|
namespace graph {
|
||||||
|
|
||||||
|
make_extension_context_t::make_extension_context_t (hb_tag_t table_tag_,
|
||||||
|
graph_t& graph_,
|
||||||
|
hb_vector_t<char>& buffer_)
|
||||||
|
: table_tag (table_tag_),
|
||||||
|
graph (graph_),
|
||||||
|
buffer (buffer_),
|
||||||
|
lookups ()
|
||||||
|
{
|
||||||
|
GSTAR* gstar = graph::GSTAR::graph_to_gstar (graph_);
|
||||||
|
if (gstar)
|
||||||
|
gstar->find_lookups (graph, lookups);
|
||||||
|
|
||||||
|
unsigned extension_size = OT::ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>::static_size;
|
||||||
|
buffer.alloc (num_non_ext_subtables () * extension_size);
|
||||||
|
}
|
||||||
|
|
||||||
unsigned make_extension_context_t::num_non_ext_subtables () {
|
unsigned make_extension_context_t::num_non_ext_subtables () {
|
||||||
unsigned count = 0;
|
unsigned count = 0;
|
||||||
for (auto l : lookups.values ())
|
for (auto l : lookups.values ())
|
||||||
|
|
|
@ -35,63 +35,16 @@ namespace graph {
|
||||||
|
|
||||||
struct Lookup;
|
struct Lookup;
|
||||||
|
|
||||||
struct GSTAR : public OT::GSUBGPOS
|
|
||||||
{
|
|
||||||
static GSTAR* graph_to_gstar (graph_t& graph)
|
|
||||||
{
|
|
||||||
return (GSTAR*) graph.root ().obj.head;
|
|
||||||
}
|
|
||||||
|
|
||||||
const void* get_lookup_list_field_offset () const
|
|
||||||
{
|
|
||||||
switch (u.version.major) {
|
|
||||||
case 1: return &(u.version1.lookupList);
|
|
||||||
#ifndef HB_NO_BORING_EXPANSION
|
|
||||||
case 2: return &(u.version2.lookupList);
|
|
||||||
#endif
|
|
||||||
default: return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void find_lookups (graph_t& graph,
|
|
||||||
hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */)
|
|
||||||
{
|
|
||||||
// TODO: template on types, based on gstar version.
|
|
||||||
unsigned lookup_list_idx = graph.index_for_offset (graph.root_idx (),
|
|
||||||
get_lookup_list_field_offset());
|
|
||||||
|
|
||||||
const OT::LookupList<SmallTypes>* lookupList =
|
|
||||||
(const OT::LookupList<SmallTypes>*) graph.object (lookup_list_idx).head;
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < lookupList->len; i++)
|
|
||||||
{
|
|
||||||
unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i]));
|
|
||||||
Lookup* lookup = (Lookup*) graph.object (lookup_idx).head;
|
|
||||||
lookups.set (lookup_idx, lookup);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct make_extension_context_t
|
struct make_extension_context_t
|
||||||
{
|
{
|
||||||
hb_tag_t table_tag;
|
hb_tag_t table_tag;
|
||||||
graph_t& graph;
|
graph_t& graph;
|
||||||
hb_vector_t<char> buffer;
|
hb_vector_t<char> buffer;
|
||||||
GSTAR* gstar;
|
|
||||||
hb_hashmap_t<unsigned, graph::Lookup*> lookups;
|
hb_hashmap_t<unsigned, graph::Lookup*> lookups;
|
||||||
|
|
||||||
make_extension_context_t (hb_tag_t table_tag_,
|
HB_INTERNAL make_extension_context_t (hb_tag_t table_tag_,
|
||||||
graph_t& graph_)
|
graph_t& graph_,
|
||||||
: table_tag (table_tag_),
|
hb_vector_t<char>& buffer_);
|
||||||
graph (graph_),
|
|
||||||
buffer (),
|
|
||||||
gstar (graph::GSTAR::graph_to_gstar (graph_)),
|
|
||||||
lookups ()
|
|
||||||
{
|
|
||||||
gstar->find_lookups (graph, lookups);
|
|
||||||
unsigned extension_size = OT::ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>::static_size;
|
|
||||||
buffer.alloc (num_non_ext_subtables () * extension_size);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool in_error () const
|
bool in_error () const
|
||||||
{
|
{
|
||||||
|
@ -99,7 +52,7 @@ struct make_extension_context_t
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
unsigned num_non_ext_subtables ();
|
HB_INTERNAL unsigned num_non_ext_subtables ();
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Lookup : public OT::Lookup
|
struct Lookup : public OT::Lookup
|
||||||
|
@ -109,6 +62,13 @@ struct Lookup : public OT::Lookup
|
||||||
return subTable.len;
|
return subTable.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool sanitize (graph_t::vertex_t& vertex) const
|
||||||
|
{
|
||||||
|
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
|
||||||
|
if (vertex_len < OT::Lookup::min_size) return false;
|
||||||
|
return vertex_len >= this->get_size ();
|
||||||
|
}
|
||||||
|
|
||||||
bool is_extension (hb_tag_t table_tag) const
|
bool is_extension (hb_tag_t table_tag) const
|
||||||
{
|
{
|
||||||
return lookupType == extension_type (table_tag);
|
return lookupType == extension_type (table_tag);
|
||||||
|
@ -123,14 +83,13 @@ struct Lookup : public OT::Lookup
|
||||||
if (!ext_type || is_extension (c.table_tag))
|
if (!ext_type || is_extension (c.table_tag))
|
||||||
{
|
{
|
||||||
// NOOP
|
// NOOP
|
||||||
printf("Already extension (obj %u).\n", this_index);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
printf("Promoting lookup type %u (obj %u) to extension.\n",
|
DEBUG_MSG (SUBSET_REPACK, nullptr,
|
||||||
type,
|
"Promoting lookup type %u (obj %u) to extension.\n",
|
||||||
this_index);
|
type,
|
||||||
|
this_index);
|
||||||
|
|
||||||
for (unsigned i = 0; i < subTable.len; i++)
|
for (unsigned i = 0; i < subTable.len; i++)
|
||||||
{
|
{
|
||||||
|
@ -149,8 +108,6 @@ struct Lookup : public OT::Lookup
|
||||||
unsigned lookup_index,
|
unsigned lookup_index,
|
||||||
unsigned subtable_index)
|
unsigned subtable_index)
|
||||||
{
|
{
|
||||||
printf(" Promoting subtable %u in lookup %u to extension.\n", subtable_index, lookup_index);
|
|
||||||
|
|
||||||
unsigned type = lookupType;
|
unsigned type = lookupType;
|
||||||
unsigned extension_size = OT::ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>::static_size;
|
unsigned extension_size = OT::ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>::static_size;
|
||||||
unsigned start = c.buffer.length;
|
unsigned start = c.buffer.length;
|
||||||
|
@ -165,9 +122,6 @@ struct Lookup : public OT::Lookup
|
||||||
extension->extensionLookupType = type;
|
extension->extensionLookupType = type;
|
||||||
extension->extensionOffset = 0;
|
extension->extensionOffset = 0;
|
||||||
|
|
||||||
unsigned type_prime = extension->extensionLookupType;
|
|
||||||
printf("Assigned type %d to extension\n", type_prime);
|
|
||||||
|
|
||||||
unsigned ext_index = c.graph.new_node (&c.buffer.arrayZ[start],
|
unsigned ext_index = c.graph.new_node (&c.buffer.arrayZ[start],
|
||||||
&c.buffer.arrayZ[end]);
|
&c.buffer.arrayZ[end]);
|
||||||
if (ext_index == (unsigned) -1) return false;
|
if (ext_index == (unsigned) -1) return false;
|
||||||
|
@ -176,11 +130,8 @@ struct Lookup : public OT::Lookup
|
||||||
for (auto& l : lookup_vertex.obj.real_links.writer ())
|
for (auto& l : lookup_vertex.obj.real_links.writer ())
|
||||||
{
|
{
|
||||||
if (l.objidx == subtable_index)
|
if (l.objidx == subtable_index)
|
||||||
{
|
|
||||||
// Change lookup to point at the extension.
|
// Change lookup to point at the extension.
|
||||||
printf(" Changing %d to %d\n", l.objidx, ext_index);
|
|
||||||
l.objidx = ext_index;
|
l.objidx = ext_index;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make extension point at the subtable.
|
// Make extension point at the subtable.
|
||||||
|
@ -213,6 +164,73 @@ struct Lookup : public OT::Lookup
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct LookupList : public OT::LookupList<T>
|
||||||
|
{
|
||||||
|
bool sanitize (const graph_t::vertex_t& vertex) const
|
||||||
|
{
|
||||||
|
int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
|
||||||
|
if (vertex_len < OT::LookupList<T>::min_size) return false;
|
||||||
|
return vertex_len >= OT::LookupList<T>::item_size * this->len;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct GSTAR : public OT::GSUBGPOS
|
||||||
|
{
|
||||||
|
static GSTAR* graph_to_gstar (graph_t& graph)
|
||||||
|
{
|
||||||
|
const auto& r = graph.root ();
|
||||||
|
|
||||||
|
GSTAR* gstar = (GSTAR*) r.obj.head;
|
||||||
|
if (!gstar->sanitize (r))
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
return gstar;
|
||||||
|
}
|
||||||
|
|
||||||
|
const void* get_lookup_list_field_offset () const
|
||||||
|
{
|
||||||
|
switch (u.version.major) {
|
||||||
|
case 1: return &(u.version1.lookupList);
|
||||||
|
#ifndef HB_NO_BORING_EXPANSION
|
||||||
|
case 2: return &(u.version2.lookupList);
|
||||||
|
#endif
|
||||||
|
default: return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool sanitize (const graph_t::vertex_t& vertex)
|
||||||
|
{
|
||||||
|
int64_t len = vertex.obj.tail - vertex.obj.head;
|
||||||
|
// Only need access to fields in min_size
|
||||||
|
return len >= OT::GSUBGPOS::min_size;
|
||||||
|
}
|
||||||
|
|
||||||
|
void find_lookups (graph_t& graph,
|
||||||
|
hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */)
|
||||||
|
{
|
||||||
|
// TODO: template on types, based on gstar version.
|
||||||
|
unsigned lookup_list_idx = graph.index_for_offset (graph.root_idx (),
|
||||||
|
get_lookup_list_field_offset());
|
||||||
|
|
||||||
|
const LookupList<SmallTypes>* lookupList =
|
||||||
|
(const LookupList<SmallTypes>*) graph.object (lookup_list_idx).head;
|
||||||
|
if (!lookupList->sanitize (graph.vertices_[lookup_list_idx]))
|
||||||
|
return;
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < lookupList->len; i++)
|
||||||
|
{
|
||||||
|
unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i]));
|
||||||
|
Lookup* lookup = (Lookup*) graph.object (lookup_idx).head;
|
||||||
|
if (!lookup->sanitize (graph.vertices_[lookup_idx])) continue;
|
||||||
|
lookups.set (lookup_idx, lookup);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif /* GRAPH_GSUBGPOS_GRAPH_HH */
|
#endif /* GRAPH_GSUBGPOS_GRAPH_HH */
|
||||||
|
|
|
@ -205,7 +205,6 @@ inline hb_blob_t* serialize (const graph_t& graph)
|
||||||
{
|
{
|
||||||
hb_vector_t<char> buffer;
|
hb_vector_t<char> buffer;
|
||||||
size_t size = graph.total_size_in_bytes ();
|
size_t size = graph.total_size_in_bytes ();
|
||||||
printf("Expected graph size: %lu.\n", size);
|
|
||||||
if (!buffer.alloc (size)) {
|
if (!buffer.alloc (size)) {
|
||||||
DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer.");
|
DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
|
@ -41,8 +41,12 @@ using graph::graph_t;
|
||||||
* docs/repacker.md
|
* docs/repacker.md
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Analyze the lookups in a GSUB/GPOS table and decide if any should be promoted
|
||||||
|
* to extension lookups.
|
||||||
|
*/
|
||||||
static inline
|
static inline
|
||||||
bool _make_extensions (graph::make_extension_context_t& ext_context)
|
bool _promote_extensions_if_needed (graph::make_extension_context_t& ext_context)
|
||||||
{
|
{
|
||||||
// TODO: Move this into graph or gsubgpos graph?
|
// TODO: Move this into graph or gsubgpos graph?
|
||||||
for (auto p : ext_context.lookups.iter ())
|
for (auto p : ext_context.lookups.iter ())
|
||||||
|
@ -50,6 +54,7 @@ bool _make_extensions (graph::make_extension_context_t& ext_context)
|
||||||
if (!p.second->make_extension (ext_context, p.first))
|
if (!p.second->make_extension (ext_context, p.first))
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,11 +174,8 @@ inline hb_blob_t*
|
||||||
hb_resolve_overflows (const T& packed,
|
hb_resolve_overflows (const T& packed,
|
||||||
hb_tag_t table_tag,
|
hb_tag_t table_tag,
|
||||||
unsigned max_rounds = 20) {
|
unsigned max_rounds = 20) {
|
||||||
printf("Resolving overflows!\n");
|
|
||||||
graph_t sorted_graph (packed);
|
graph_t sorted_graph (packed);
|
||||||
sorted_graph.sort_shortest_distance ();
|
sorted_graph.sort_shortest_distance ();
|
||||||
printf("Initial graph size: %lu.\n",
|
|
||||||
sorted_graph.total_size_in_bytes ());
|
|
||||||
|
|
||||||
bool will_overflow = graph::will_overflow (sorted_graph);
|
bool will_overflow = graph::will_overflow (sorted_graph);
|
||||||
if (!will_overflow)
|
if (!will_overflow)
|
||||||
|
@ -181,16 +183,18 @@ hb_resolve_overflows (const T& packed,
|
||||||
return graph::serialize (sorted_graph);
|
return graph::serialize (sorted_graph);
|
||||||
}
|
}
|
||||||
|
|
||||||
graph::make_extension_context_t ext_context (table_tag, sorted_graph);
|
hb_vector_t<char> extension_buffer; // Needs to live until serialization is done.
|
||||||
if (ext_context.in_error ())
|
|
||||||
return nullptr;
|
|
||||||
|
|
||||||
if ((table_tag == HB_OT_TAG_GPOS
|
if ((table_tag == HB_OT_TAG_GPOS
|
||||||
|| table_tag == HB_OT_TAG_GSUB)
|
|| table_tag == HB_OT_TAG_GSUB)
|
||||||
&& will_overflow)
|
&& will_overflow)
|
||||||
{
|
{
|
||||||
if (!_make_extensions (ext_context)) {
|
graph::make_extension_context_t ext_context (table_tag, sorted_graph, extension_buffer);
|
||||||
printf("make extensions failed.\n");
|
if (ext_context.in_error ())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
if (1 && !_promote_extensions_if_needed (ext_context)) {
|
||||||
|
DEBUG_MSG (SUBSET_REPACK, nullptr, "Extensions promotion failed.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue