tor-browser

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

markbasepos-graph.hh (17158B)


      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_MARKBASEPOS_GRAPH_HH
     28 #define GRAPH_MARKBASEPOS_GRAPH_HH
     29 
     30 #include "split-helpers.hh"
     31 #include "coverage-graph.hh"
     32 #include "../OT/Layout/GPOS/MarkBasePos.hh"
     33 #include "../OT/Layout/GPOS/PosLookupSubTable.hh"
     34 
     35 namespace graph {
     36 
     37 struct AnchorMatrix : public OT::Layout::GPOS_impl::AnchorMatrix
     38 {
     39  bool sanitize (graph_t::vertex_t& vertex, unsigned class_count) const
     40  {
     41    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
     42    if (vertex_len < AnchorMatrix::min_size) return false;
     43    hb_barrier ();
     44 
     45    return vertex_len >= AnchorMatrix::min_size +
     46        OT::Offset16::static_size * class_count * this->rows;
     47  }
     48 
     49  bool shrink (gsubgpos_graph_context_t& c,
     50               unsigned this_index,
     51               unsigned old_class_count,
     52               unsigned new_class_count)
     53  {
     54    if (new_class_count >= old_class_count) return false;
     55    auto& o = c.graph.vertices_[this_index].obj;
     56    unsigned base_count = rows;
     57    o.tail = o.head +
     58             AnchorMatrix::min_size +
     59             OT::Offset16::static_size * base_count * new_class_count;
     60 
     61    // Reposition links into the new indexing scheme.
     62    for (auto& link : o.real_links.writer ())
     63    {
     64      unsigned index = (link.position - 2) / 2;
     65      unsigned base = index / old_class_count;
     66      unsigned klass = index % old_class_count;
     67      if (klass >= new_class_count)
     68        // should have already been removed
     69        return false;
     70 
     71      unsigned new_index = base * new_class_count + klass;
     72 
     73      link.position = (char*) &(this->matrixZ[new_index]) - (char*) this;
     74    }
     75 
     76    return true;
     77  }
     78 
     79  unsigned clone (gsubgpos_graph_context_t& c,
     80                  unsigned this_index,
     81                  unsigned start,
     82                  unsigned end,
     83                  unsigned class_count)
     84  {
     85    unsigned base_count = rows;
     86    unsigned new_class_count = end - start;
     87    unsigned size = AnchorMatrix::min_size +
     88                    OT::Offset16::static_size * new_class_count * rows;
     89    unsigned prime_id = c.create_node (size);
     90    if (prime_id == (unsigned) -1) return -1;
     91    AnchorMatrix* prime = (AnchorMatrix*) c.graph.object (prime_id).head;
     92    prime->rows = base_count;
     93 
     94    auto& o = c.graph.vertices_[this_index].obj;
     95    int num_links = o.real_links.length;
     96    for (int i = 0; i < num_links; i++)
     97    {
     98      const auto& link = o.real_links[i];
     99      unsigned old_index = (link.position - 2) / OT::Offset16::static_size;
    100      unsigned klass = old_index % class_count;
    101      if (klass < start || klass >= end) continue;
    102 
    103      unsigned base = old_index / class_count;
    104      unsigned new_klass = klass - start;
    105      unsigned new_index = base * new_class_count + new_klass;
    106 
    107 
    108      unsigned child_idx = link.objidx;
    109      c.graph.add_link (&(prime->matrixZ[new_index]),
    110                        prime_id,
    111                        child_idx);
    112 
    113      auto& child = c.graph.vertices_[child_idx];
    114      child.remove_parent (this_index);
    115 
    116      o.real_links.remove_unordered (i);
    117      num_links--;
    118      i--;
    119    }
    120 
    121    return prime_id;
    122  }
    123 };
    124 
    125 struct MarkArray : public OT::Layout::GPOS_impl::MarkArray
    126 {
    127  bool sanitize (graph_t::vertex_t& vertex) const
    128  {
    129    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
    130    unsigned min_size = MarkArray::min_size;
    131    if (vertex_len < min_size) return false;
    132    hb_barrier ();
    133 
    134    return vertex_len >= get_size ();
    135  }
    136 
    137  bool shrink (gsubgpos_graph_context_t& c,
    138               const hb_hashmap_t<unsigned, unsigned>& mark_array_links,
    139               unsigned this_index,
    140               unsigned new_class_count)
    141  {
    142    auto& o = c.graph.vertices_[this_index].obj;
    143    for (const auto& link : o.real_links)
    144      c.graph.vertices_[link.objidx].remove_parent (this_index);
    145    o.real_links.reset ();
    146 
    147    unsigned new_index = 0;
    148    for (const auto& record : this->iter ())
    149    {
    150      unsigned klass = record.klass;
    151      if (klass >= new_class_count) continue;
    152 
    153      (*this)[new_index].klass = klass;
    154      unsigned position = (char*) &record.markAnchor - (char*) this;
    155      unsigned* objidx;
    156      if (!mark_array_links.has (position, &objidx))
    157      {
    158        new_index++;
    159        continue;
    160      }
    161 
    162      c.graph.add_link (&(*this)[new_index].markAnchor, this_index, *objidx);
    163      new_index++;
    164    }
    165 
    166    this->len = new_index;
    167    o.tail = o.head + MarkArray::min_size +
    168             OT::Layout::GPOS_impl::MarkRecord::static_size * new_index;
    169    return true;
    170  }
    171 
    172  unsigned clone (gsubgpos_graph_context_t& c,
    173                  unsigned this_index,
    174                  const hb_hashmap_t<unsigned, unsigned>& pos_to_index,
    175                  hb_set_t& marks,
    176                  unsigned start_class)
    177  {
    178    unsigned size = MarkArray::min_size +
    179                    OT::Layout::GPOS_impl::MarkRecord::static_size *
    180                    marks.get_population ();
    181    unsigned prime_id = c.create_node (size);
    182    if (prime_id == (unsigned) -1) return -1;
    183    MarkArray* prime = (MarkArray*) c.graph.object (prime_id).head;
    184    prime->len = marks.get_population ();
    185 
    186 
    187    unsigned i = 0;
    188    for (hb_codepoint_t mark : marks)
    189    {
    190      (*prime)[i].klass = (*this)[mark].klass - start_class;
    191      unsigned offset_pos = (char*) &((*this)[mark].markAnchor) - (char*) this;
    192      unsigned* anchor_index;
    193      if (pos_to_index.has (offset_pos, &anchor_index))
    194        c.graph.move_child (this_index,
    195                            &((*this)[mark].markAnchor),
    196                            prime_id,
    197                            &((*prime)[i].markAnchor));
    198 
    199      i++;
    200    }
    201 
    202    return prime_id;
    203  }
    204 };
    205 
    206 struct MarkBasePosFormat1 : public OT::Layout::GPOS_impl::MarkBasePosFormat1_2<SmallTypes>
    207 {
    208  bool sanitize (graph_t::vertex_t& vertex) const
    209  {
    210    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
    211    return vertex_len >= MarkBasePosFormat1::static_size;
    212  }
    213 
    214  hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
    215                                         unsigned this_index)
    216  {
    217    hb_set_t visited;
    218 
    219    const unsigned base_coverage_id = c.graph.index_for_offset (this_index, &baseCoverage);
    220    const unsigned base_size =
    221        OT::Layout::GPOS_impl::MarkBasePosFormat1_2<SmallTypes>::min_size +
    222        MarkArray::min_size +
    223        AnchorMatrix::min_size +
    224        c.graph.vertices_[base_coverage_id].table_size ();
    225 
    226    hb_vector_t<class_info_t> class_to_info = get_class_info (c, this_index);
    227 
    228    unsigned class_count = classCount;
    229    auto base_array = c.graph.as_table<AnchorMatrix> (this_index,
    230                                                      &baseArray,
    231                                                      class_count);
    232    if (!base_array) return hb_vector_t<unsigned> ();
    233    unsigned base_count = base_array.table->rows;
    234 
    235    unsigned partial_coverage_size = 4;
    236    unsigned accumulated = base_size;
    237    hb_vector_t<unsigned> split_points;
    238 
    239    for (unsigned klass = 0; klass < class_count; klass++)
    240    {
    241      class_info_t& info = class_to_info[klass];
    242      partial_coverage_size += OT::HBUINT16::static_size * info.marks.get_population ();
    243      unsigned accumulated_delta =
    244          OT::Layout::GPOS_impl::MarkRecord::static_size * info.marks.get_population () +
    245          OT::Offset16::static_size * base_count;
    246 
    247      for (unsigned objidx : info.child_indices)
    248        accumulated_delta += c.graph.find_subgraph_size (objidx, visited);
    249 
    250      accumulated += accumulated_delta;
    251      unsigned total = accumulated + partial_coverage_size;
    252 
    253      if (total >= (1 << 16))
    254      {
    255        split_points.push (klass);
    256        accumulated = base_size + accumulated_delta;
    257        partial_coverage_size = 4 + OT::HBUINT16::static_size * info.marks.get_population ();
    258        visited.clear (); // node sharing isn't allowed between splits.
    259      }
    260    }
    261 
    262 
    263    const unsigned mark_array_id = c.graph.index_for_offset (this_index, &markArray);
    264    split_context_t split_context {
    265      c,
    266      this,
    267      this_index,
    268      std::move (class_to_info),
    269      c.graph.vertices_[mark_array_id].position_to_index_map (),
    270    };
    271 
    272    return actuate_subtable_split<split_context_t> (split_context, split_points);
    273  }
    274 
    275 private:
    276 
    277  struct class_info_t {
    278    hb_set_t marks;
    279    hb_vector_t<unsigned> child_indices;
    280  };
    281 
    282  struct split_context_t {
    283    gsubgpos_graph_context_t& c;
    284    MarkBasePosFormat1* thiz;
    285    unsigned this_index;
    286    hb_vector_t<class_info_t> class_to_info;
    287    hb_hashmap_t<unsigned, unsigned> mark_array_links;
    288 
    289    hb_set_t marks_for (unsigned start, unsigned end)
    290    {
    291      hb_set_t marks;
    292      for (unsigned klass = start; klass < end; klass++)
    293      {
    294        + class_to_info[klass].marks.iter ()
    295        | hb_sink (marks)
    296        ;
    297      }
    298      return marks;
    299    }
    300 
    301    unsigned original_count ()
    302    {
    303      return thiz->classCount;
    304    }
    305 
    306    unsigned clone_range (unsigned start, unsigned end)
    307    {
    308      return thiz->clone_range (*this, this->this_index, start, end);
    309    }
    310 
    311    bool shrink (unsigned count)
    312    {
    313      return thiz->shrink (*this, this->this_index, count);
    314    }
    315  };
    316 
    317  hb_vector_t<class_info_t> get_class_info (gsubgpos_graph_context_t& c,
    318                                            unsigned this_index)
    319  {
    320    hb_vector_t<class_info_t> class_to_info;
    321 
    322    unsigned class_count = classCount;
    323    if (!class_count) return class_to_info;
    324 
    325    if (!class_to_info.resize (class_count))
    326      return hb_vector_t<class_info_t>();
    327 
    328    auto mark_array = c.graph.as_table<MarkArray> (this_index, &markArray);
    329    if (!mark_array) return hb_vector_t<class_info_t> ();
    330    unsigned mark_count = mark_array.table->len;
    331    for (unsigned mark = 0; mark < mark_count; mark++)
    332    {
    333      unsigned klass = (*mark_array.table)[mark].get_class ();
    334      if (klass >= class_count) continue;
    335      class_to_info[klass].marks.add (mark);
    336    }
    337 
    338    for (const auto& link : mark_array.vertex->obj.real_links)
    339    {
    340      unsigned mark = (link.position - 2) /
    341                     OT::Layout::GPOS_impl::MarkRecord::static_size;
    342      unsigned klass = (*mark_array.table)[mark].get_class ();
    343      if (klass >= class_count) continue;
    344      class_to_info[klass].child_indices.push (link.objidx);
    345    }
    346 
    347    unsigned base_array_id =
    348        c.graph.index_for_offset (this_index, &baseArray);
    349    auto& base_array_v = c.graph.vertices_[base_array_id];
    350 
    351    for (const auto& link : base_array_v.obj.real_links)
    352    {
    353      unsigned index = (link.position - 2) / OT::Offset16::static_size;
    354      unsigned klass = index % class_count;
    355      class_to_info[klass].child_indices.push (link.objidx);
    356    }
    357 
    358    return class_to_info;
    359  }
    360 
    361  bool shrink (split_context_t& sc,
    362               unsigned this_index,
    363               unsigned count)
    364  {
    365    DEBUG_MSG (SUBSET_REPACK, nullptr,
    366               "  Shrinking MarkBasePosFormat1 (%u) to [0, %u).",
    367               this_index,
    368               count);
    369 
    370    unsigned old_count = classCount;
    371    if (count >= old_count)
    372      return true;
    373 
    374    classCount = count;
    375 
    376    auto mark_coverage = sc.c.graph.as_mutable_table<Coverage> (this_index,
    377                                                                &markCoverage);
    378    if (!mark_coverage) return false;
    379    hb_set_t marks = sc.marks_for (0, count);
    380    auto new_coverage =
    381        + hb_enumerate (mark_coverage.table->iter ())
    382        | hb_filter (marks, hb_first)
    383        | hb_map_retains_sorting (hb_second)
    384        ;
    385    if (!Coverage::make_coverage (sc.c, + new_coverage,
    386                                  mark_coverage.index,
    387                                  4 + 2 * marks.get_population ()))
    388      return false;
    389 
    390 
    391    auto base_array = sc.c.graph.as_mutable_table<AnchorMatrix> (this_index,
    392                                                                 &baseArray,
    393                                                                 old_count);
    394    if (!base_array || !base_array.table->shrink (sc.c,
    395                                                  base_array.index,
    396                                                  old_count,
    397                                                  count))
    398      return false;
    399 
    400    auto mark_array = sc.c.graph.as_mutable_table<MarkArray> (this_index,
    401                                                              &markArray);
    402    if (!mark_array || !mark_array.table->shrink (sc.c,
    403                                                  sc.mark_array_links,
    404                                                  mark_array.index,
    405                                                  count))
    406      return false;
    407 
    408    return true;
    409  }
    410 
    411  // Create a new MarkBasePos that has all of the data for classes from [start, end).
    412  unsigned clone_range (split_context_t& sc,
    413                        unsigned this_index,
    414                        unsigned start, unsigned end) const
    415  {
    416    DEBUG_MSG (SUBSET_REPACK, nullptr,
    417               "  Cloning MarkBasePosFormat1 (%u) range [%u, %u).", this_index, start, end);
    418 
    419    graph_t& graph = sc.c.graph;
    420    unsigned prime_size = OT::Layout::GPOS_impl::MarkBasePosFormat1_2<SmallTypes>::static_size;
    421 
    422    unsigned prime_id = sc.c.create_node (prime_size);
    423    if (prime_id == (unsigned) -1) return -1;
    424 
    425    MarkBasePosFormat1* prime = (MarkBasePosFormat1*) graph.object (prime_id).head;
    426    prime->format = this->format;
    427    unsigned new_class_count = end - start;
    428    prime->classCount = new_class_count;
    429 
    430    unsigned base_coverage_id =
    431        graph.index_for_offset (sc.this_index, &baseCoverage);
    432    graph.add_link (&(prime->baseCoverage), prime_id, base_coverage_id);
    433    graph.duplicate (prime_id, base_coverage_id);
    434 
    435    auto mark_coverage = sc.c.graph.as_table<Coverage> (this_index,
    436                                                        &markCoverage);
    437    if (!mark_coverage) return false;
    438    hb_set_t marks = sc.marks_for (start, end);
    439    auto new_coverage =
    440        + hb_enumerate (mark_coverage.table->iter ())
    441        | hb_filter (marks, hb_first)
    442        | hb_map_retains_sorting (hb_second)
    443        ;
    444    if (!Coverage::add_coverage (sc.c,
    445                                 prime_id,
    446                                 2,
    447                                 + new_coverage,
    448                                 marks.get_population () * 2 + 4))
    449      return -1;
    450 
    451    auto mark_array =
    452        graph.as_table <MarkArray> (sc.this_index, &markArray);
    453    if (!mark_array) return -1;
    454    unsigned new_mark_array =
    455        mark_array.table->clone (sc.c,
    456                                 mark_array.index,
    457                                 sc.mark_array_links,
    458                                 marks,
    459                                 start);
    460    graph.add_link (&(prime->markArray), prime_id, new_mark_array);
    461 
    462    unsigned class_count = classCount;
    463    auto base_array =
    464        graph.as_table<AnchorMatrix> (sc.this_index, &baseArray, class_count);
    465    if (!base_array) return -1;
    466    unsigned new_base_array =
    467        base_array.table->clone (sc.c,
    468                                 base_array.index,
    469                                 start, end, this->classCount);
    470    graph.add_link (&(prime->baseArray), prime_id, new_base_array);
    471 
    472    return prime_id;
    473  }
    474 };
    475 
    476 
    477 struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos
    478 {
    479  hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
    480                                         unsigned this_index)
    481  {
    482    switch (u.format.v) {
    483    case 1:
    484      return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, this_index);
    485 #ifndef HB_NO_BEYOND_64K
    486    case 2: HB_FALLTHROUGH;
    487      // Don't split 24bit MarkBasePos's.
    488 #endif
    489    default:
    490      return hb_vector_t<unsigned> ();
    491    }
    492  }
    493 
    494  bool sanitize (graph_t::vertex_t& vertex) const
    495  {
    496    int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
    497    if (vertex_len < u.format.v.get_size ()) return false;
    498    hb_barrier ();
    499 
    500    switch (u.format.v) {
    501    case 1:
    502      return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex);
    503 #ifndef HB_NO_BEYOND_64K
    504    case 2: HB_FALLTHROUGH;
    505 #endif
    506    default:
    507      // We don't handle format 3 and 4 here.
    508      return false;
    509    }
    510  }
    511 };
    512 
    513 
    514 }
    515 
    516 #endif  // GRAPH_MARKBASEPOS_GRAPH_HH