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