tor-browser

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

serialize.hh (8361B)


      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 #ifndef GRAPH_SERIALIZE_HH
     28 #define GRAPH_SERIALIZE_HH
     29 
     30 namespace graph {
     31 
     32 struct overflow_record_t
     33 {
     34  unsigned parent;
     35  unsigned child;
     36 
     37  bool operator != (const overflow_record_t o) const
     38  { return !(*this == o); }
     39 
     40  inline bool operator == (const overflow_record_t& o) const
     41  {
     42    return parent == o.parent &&
     43        child == o.child;
     44  }
     45 
     46  inline uint32_t hash () const
     47  {
     48    uint32_t current = 0;
     49    current = current * 31 + hb_hash (parent);
     50    current = current * 31 + hb_hash (child);
     51    return current;
     52  }
     53 };
     54 
     55 inline
     56 int64_t compute_offset (
     57    const graph_t& graph,
     58    unsigned parent_idx,
     59    const hb_serialize_context_t::object_t::link_t& link)
     60 {
     61  const auto& parent = graph.vertices_[parent_idx];
     62  const auto& child = graph.vertices_[link.objidx];
     63  int64_t offset = 0;
     64  switch ((hb_serialize_context_t::whence_t) link.whence) {
     65    case hb_serialize_context_t::whence_t::Head:
     66      offset = child.start - parent.start; break;
     67    case hb_serialize_context_t::whence_t::Tail:
     68      offset = child.start - parent.end; break;
     69    case hb_serialize_context_t::whence_t::Absolute:
     70      offset = child.start; break;
     71  }
     72 
     73  assert (offset >= link.bias);
     74  offset -= link.bias;
     75  return offset;
     76 }
     77 
     78 inline
     79 bool is_valid_offset (int64_t offset,
     80                      const hb_serialize_context_t::object_t::link_t& link)
     81 {
     82  if (unlikely (!link.width))
     83    // Virtual links can't overflow.
     84    return link.is_signed || offset >= 0;
     85 
     86  if (link.is_signed)
     87  {
     88    if (link.width == 4)
     89      return offset >= -((int64_t) 1 << 31) && offset < ((int64_t) 1 << 31);
     90    else
     91      return offset >= -(1 << 15) && offset < (1 << 15);
     92  }
     93  else
     94  {
     95    if (link.width == 4)
     96      return offset >= 0 && offset < ((int64_t) 1 << 32);
     97    else if (link.width == 3)
     98      return offset >= 0 && offset < ((int32_t) 1 << 24);
     99    else
    100      return offset >= 0 && offset < (1 << 16);
    101  }
    102 }
    103 
    104 /*
    105 * Will any offsets overflow on graph when it's serialized?
    106 */
    107 inline bool
    108 will_overflow (graph_t& graph,
    109               hb_vector_t<overflow_record_t>* overflows = nullptr)
    110 {
    111  if (overflows) overflows->resize (0);
    112  graph.update_positions ();
    113 
    114  hb_hashmap_t<overflow_record_t*, bool> record_set;
    115  const auto& vertices = graph.vertices_;
    116  for (unsigned parent_idx : graph.ordering_)
    117  {
    118    // Don't need to check virtual links for overflow
    119    for (const auto& link : vertices.arrayZ[parent_idx].obj.real_links)
    120    {
    121      int64_t offset = compute_offset (graph, parent_idx, link);
    122      if (likely (is_valid_offset (offset, link)))
    123        continue;
    124 
    125      if (!overflows) return true;
    126 
    127      overflow_record_t r;
    128      r.parent = parent_idx;
    129      r.child = link.objidx;
    130      if (record_set.has(&r)) continue; // don't keep duplicate overflows.
    131 
    132      overflows->push (r);
    133      record_set.set(&r, true);
    134    }
    135  }
    136 
    137  if (!overflows) return false;
    138  return overflows->length;
    139 }
    140 
    141 inline
    142 void print_overflows (graph_t& graph,
    143                      const hb_vector_t<overflow_record_t>& overflows)
    144 {
    145  if (!DEBUG_ENABLED(SUBSET_REPACK)) return;
    146 
    147  graph.update_parents ();
    148  int limit = 10;
    149  for (const auto& o : overflows)
    150  {
    151    if (!limit--) break;
    152    const auto& parent = graph.vertices_[o.parent];
    153    const auto& child = graph.vertices_[o.child];
    154    DEBUG_MSG (SUBSET_REPACK, nullptr,
    155               "  overflow from "
    156               "%4u (%4u in, %4u out, space %2u) => "
    157               "%4u (%4u in, %4u out, space %2u)",
    158               o.parent,
    159               parent.incoming_edges (),
    160               parent.obj.real_links.length + parent.obj.virtual_links.length,
    161               graph.space_for (o.parent),
    162               o.child,
    163               child.incoming_edges (),
    164               child.obj.real_links.length + child.obj.virtual_links.length,
    165               graph.space_for (o.child));
    166  }
    167  if (overflows.length > 10) {
    168    DEBUG_MSG (SUBSET_REPACK, nullptr, "  ... plus %u more overflows.", overflows.length - 10);
    169  }
    170 }
    171 
    172 template <typename O> inline void
    173 serialize_link_of_type (const hb_serialize_context_t::object_t::link_t& link,
    174                        char* head,
    175                        unsigned size,
    176                        const hb_vector_t<unsigned>& id_map,
    177                        hb_serialize_context_t* c)
    178 {
    179  assert(link.position + link.width <= size);
    180 
    181  OT::Offset<O>* offset = reinterpret_cast<OT::Offset<O>*> (head + link.position);
    182  *offset = 0;
    183  c->add_link (*offset,
    184               id_map[link.objidx],
    185               (hb_serialize_context_t::whence_t) link.whence,
    186               link.bias);
    187 }
    188 
    189 inline
    190 void serialize_link (const hb_serialize_context_t::object_t::link_t& link,
    191                     char* head,
    192                     unsigned size,
    193                     const hb_vector_t<unsigned>& id_map,
    194                     hb_serialize_context_t* c)
    195 {
    196  switch (link.width)
    197  {
    198    case 0:
    199      // Virtual links aren't serialized.
    200      return;
    201    case 4:
    202      if (link.is_signed)
    203      {
    204        serialize_link_of_type<OT::HBINT32> (link, head, size, id_map, c);
    205      } else {
    206        serialize_link_of_type<OT::HBUINT32> (link, head, size, id_map, c);
    207      }
    208      return;
    209    case 2:
    210      if (link.is_signed)
    211      {
    212        serialize_link_of_type<OT::HBINT16> (link, head, size, id_map, c);
    213      } else {
    214        serialize_link_of_type<OT::HBUINT16> (link, head, size, id_map, c);
    215      }
    216      return;
    217    case 3:
    218      serialize_link_of_type<OT::HBUINT24> (link, head, size, id_map, c);
    219      return;
    220    default:
    221      // Unexpected link width.
    222      assert (0);
    223  }
    224 }
    225 
    226 /*
    227 * serialize graph into the provided serialization buffer.
    228 */
    229 inline hb_blob_t* serialize (const graph_t& graph)
    230 {
    231  hb_vector_t<char> buffer;
    232  size_t size = graph.total_size_in_bytes ();
    233 
    234  if (!size) return hb_blob_get_empty ();
    235 
    236  if (!buffer.alloc (size)) {
    237    DEBUG_MSG (SUBSET_REPACK, nullptr, "Unable to allocate output buffer.");
    238    return nullptr;
    239  }
    240  hb_serialize_context_t c((void *) buffer, size);
    241 
    242  c.start_serialize<void> ();
    243  const auto& vertices = graph.vertices_;
    244 
    245  // Objects are placed in the serializer in reverse order since children need
    246  // to be inserted before their parents.
    247 
    248  // Maps from our obj id's to the id's used during this serialization.
    249  hb_vector_t<unsigned> id_map;
    250  id_map.resize(graph.ordering_.length);
    251  for (int pos = graph.ordering_.length - 1; pos >= 0; pos--) {
    252    unsigned i = graph.ordering_[pos];
    253    c.push ();
    254 
    255    auto& v = vertices[i];
    256 
    257    size_t size = v.obj.tail - v.obj.head;
    258 
    259    char* start = c.allocate_size <char> (size);
    260    if (!start) {
    261      DEBUG_MSG (SUBSET_REPACK, nullptr, "Buffer out of space.");
    262      return nullptr;
    263    }
    264 
    265    hb_memcpy (start, v.obj.head, size);
    266 
    267    // Only real links needs to be serialized.
    268    for (const auto& link : v.obj.real_links)
    269      serialize_link (link, start, size, id_map, &c);
    270 
    271    // All duplications are already encoded in the graph, so don't
    272    // enable sharing during packing.
    273    id_map[i] = c.pop_pack (false);
    274  }
    275  c.end_serialize ();
    276 
    277  if (c.in_error ()) {
    278    DEBUG_MSG (SUBSET_REPACK, nullptr, "Error during serialization. Err flag: %d",
    279               c.errors);
    280    return nullptr;
    281  }
    282 
    283  return c.copy_blob ();
    284 }
    285 
    286 } // namespace graph
    287 
    288 #endif // GRAPH_SERIALIZE_HH