/*
* Copyright © 2022 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 GRAPH_SERIALIZE_HH
#define GRAPH_SERIALIZE_HH
namespace graph {
struct overflow_record_t
{
unsigned parent;
unsigned child;
bool operator != (const overflow_record_t o) const
{ return !(*this == o); }
inline bool operator == (const overflow_record_t& o) const
{
return parent == o.parent &&
child == o.child;
}
inline uint32_t hash () const
{
uint32_t current = 0 ;
current = current * 31 + hb_hash (parent);
current = current * 31 + hb_hash (child);
return current;
}
};
inline
int64_t compute_offset (
const graph_t& graph,
unsigned parent_idx,
const hb_serialize_context_t::object_t::link_t& link)
{
const auto & parent = graph.vertices_[parent_idx];
const auto & child = graph.vertices_[link.objidx];
int64_t offset = 0 ;
switch ((hb_serialize_context_t::whence_t) link.whence) {
case hb_serialize_context_t::whence_t::Head:
offset = child.start - parent.start; break ;
case hb_serialize_context_t::whence_t::Tail:
offset = child.start - parent.end; break ;
case hb_serialize_context_t::whence_t::Absolute:
offset = child.start; break ;
}
assert (offset >= link.bias);
offset -= link.bias;
return offset;
}
inline
bool is_valid_offset (int64_t offset,
const hb_serialize_context_t::object_t::link_t& link)
{
if (unlikely (!link.width))
// Virtual links can't overflow.
return link.is_signed || offset >= 0 ;
if (link.is_signed)
{
if (link.width == 4 )
return offset >= -((int64_t) 1 << 31 ) && offset < ((int64_t) 1 << 31 );
else
return offset >= -(1 << 15 ) && offset < (1 << 15 );
}
else
{
if (link.width == 4 )
return offset >= 0 && offset < ((int64_t) 1 << 32 );
else if (link.width == 3 )
return offset >= 0 && offset < ((int32_t) 1 << 24 );
else
return offset >= 0 && offset < (1 << 16 );
}
}
/*
* Will any offsets overflow on graph when it ' s serialized ?
*/
inline bool
will_overflow (graph_t& graph,
hb_vector_t<overflow_record_t>* overflows = nullptr)
{
if (overflows) overflows->resize (0 );
graph.update_positions ();
hb_hashmap_t<overflow_record_t*, bool > record_set;
const auto & vertices = graph.vertices_;
for (int parent_idx = vertices.length - 1 ; parent_idx >= 0 ; parent_idx--)
{
// Don't need to check virtual links for overflow
for (const auto & link : vertices.arrayZ[parent_idx].obj.real_links)
{
int64_t offset = compute_offset (graph, parent_idx, link);
if (likely (is_valid_offset (offset, link)))
continue ;
if (!overflows) return true ;
overflow_record_t r;
r.parent = parent_idx;
r.child = link.objidx;
if (record_set.has(&r)) continue ; // don't keep duplicate overflows.
overflows->push (r);
record_set.set(&r, true );
}
}
if (!overflows) return false ;
return overflows->length;
}
inline
void print_overflows (graph_t& graph,
const hb_vector_t<overflow_record_t>& overflows)
{
if (!DEBUG_ENABLED(SUBSET_REPACK)) return ;
graph.update_parents ();
int limit = 10 ;
for (const auto & o : overflows)
{
if (!limit--) break ;
const auto & parent = graph.vertices_[o.parent];
const auto & child = graph.vertices_[o.child];
DEBUG_MSG (SUBSET_REPACK, nullptr,
" overflow from "
"%4u (%4u in, %4u out, space %2u) => "
"%4u (%4u in, %4u out, space %2u)" ,
o.parent,
parent.incoming_edges (),
parent.obj.real_links.length + parent.obj.virtual_links.length,
graph.space_for (o.parent),
o.child,
child.incoming_edges (),
child.obj.real_links.length + child.obj.virtual_links.length,
graph.space_for (o.child));
}
if (overflows.length > 10 ) {
DEBUG_MSG (SUBSET_REPACK, nullptr, " ... plus %u more overflows." , overflows.length - 10 );
}
}
template <typename O> inline 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,
// serializer has an extra nil object at the start of the
// object array. So all id's are +1 of what our id's are.
link.objidx + 1 ,
(hb_serialize_context_t::whence_t) link.whence,
link.bias);
}
inline
void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
char * head,
hb_serialize_context_t* c)
{
switch (link.width)
{
case 0 :
// Virtual links aren't serialized.
return ;
case 4 :
if (link.is_signed)
{
serialize_link_of_type<OT::HBINT32> (link, head, c);
} else {
serialize_link_of_type<OT::HBUINT32> (link, head, c);
}
return ;
case 2 :
if (link.is_signed)
{
serialize_link_of_type<OT::HBINT16> (link, head, c);
} else {
serialize_link_of_type<OT::HBUINT16> (link, head, c);
}
return ;
case 3 :
serialize_link_of_type<OT::HBUINT24> (link, head, c);
return ;
default :
// Unexpected link width.
assert (0 );
}
}
/*
* serialize graph into the provided serialization buffer .
*/
inline hb_blob_t* serialize (const graph_t& graph)
{
hb_vector_t<char > buffer;
size_t size = graph.total_size_in_bytes ();
if (!size) return hb_blob_get_empty ();
if (!buffer.alloc (size)) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer." );
return nullptr;
}
hb_serialize_context_t c((void *) buffer, size);
c.start_serialize<void > ();
const auto & vertices = graph.vertices_;
for (unsigned i = 0 ; i < vertices.length; i++) {
c.push ();
size_t size = vertices[i].obj.tail - vertices[i].obj.head;
char * start = c.allocate_size <char > (size);
if (!start) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space." );
return nullptr;
}
hb_memcpy (start, vertices[i].obj.head, size);
// Only real links needs to be serialized.
for (const auto & link : vertices[i].obj.real_links)
serialize_link (link, start, &c);
// All duplications are already encoded in the graph, so don't
// enable sharing during packing.
c.pop_pack (false );
}
c.end_serialize ();
if (c.in_error ()) {
DEBUG_MSG (SUBSET_REPACK, nullptr, "Error during serialization. Err flag: %d" ,
c.errors);
return nullptr;
}
return c.copy_blob ();
}
} // namespace graph
#endif // GRAPH_SERIALIZE_HH
Messung V0.5 in Prozent C=96 H=96 G=95
¤ Dauer der Verarbeitung: 0.11 Sekunden
(vorverarbeitet am 2026-06-10)
¤
*© Formatika GbR, Deutschland