xref: /aosp_15_r20/external/harfbuzz_ng/src/graph/markbasepos-graph.hh (revision 2d1272b857b1f7575e6e246373e1cb218663db8a)
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     hb_barrier ();
44 
45     return vertex_len >= AnchorMatrix::min_size +
46         OT::Offset16::static_size * class_count * this->rows;
47   }
48 
shrinkgraph::AnchorMatrix49   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 
clonegraph::AnchorMatrix79   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 {
sanitizegraph::MarkArray127   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 
shrinkgraph::MarkArray137   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 
clonegraph::MarkArray172   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 {
sanitizegraph::MarkBasePosFormat1208   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 
split_subtablesgraph::MarkBasePosFormat1214   hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
215                                          unsigned parent_index,
216                                          unsigned this_index)
217   {
218     hb_set_t visited;
219 
220     const unsigned base_coverage_id = c.graph.index_for_offset (this_index, &baseCoverage);
221     const unsigned base_size =
222         OT::Layout::GPOS_impl::MarkBasePosFormat1_2<SmallTypes>::min_size +
223         MarkArray::min_size +
224         AnchorMatrix::min_size +
225         c.graph.vertices_[base_coverage_id].table_size ();
226 
227     hb_vector_t<class_info_t> class_to_info = get_class_info (c, this_index);
228 
229     unsigned class_count = classCount;
230     auto base_array = c.graph.as_table<AnchorMatrix> (this_index,
231                                                       &baseArray,
232                                                       class_count);
233     if (!base_array) return hb_vector_t<unsigned> ();
234     unsigned base_count = base_array.table->rows;
235 
236     unsigned partial_coverage_size = 4;
237     unsigned accumulated = base_size;
238     hb_vector_t<unsigned> split_points;
239 
240     for (unsigned klass = 0; klass < class_count; klass++)
241     {
242       class_info_t& info = class_to_info[klass];
243       partial_coverage_size += OT::HBUINT16::static_size * info.marks.get_population ();
244       unsigned accumulated_delta =
245           OT::Layout::GPOS_impl::MarkRecord::static_size * info.marks.get_population () +
246           OT::Offset16::static_size * base_count;
247 
248       for (unsigned objidx : info.child_indices)
249         accumulated_delta += c.graph.find_subgraph_size (objidx, visited);
250 
251       accumulated += accumulated_delta;
252       unsigned total = accumulated + partial_coverage_size;
253 
254       if (total >= (1 << 16))
255       {
256         split_points.push (klass);
257         accumulated = base_size + accumulated_delta;
258         partial_coverage_size = 4 + OT::HBUINT16::static_size * info.marks.get_population ();
259         visited.clear (); // node sharing isn't allowed between splits.
260       }
261     }
262 
263 
264     const unsigned mark_array_id = c.graph.index_for_offset (this_index, &markArray);
265     split_context_t split_context {
266       c,
267       this,
268       c.graph.duplicate_if_shared (parent_index, this_index),
269       std::move (class_to_info),
270       c.graph.vertices_[mark_array_id].position_to_index_map (),
271     };
272 
273     return actuate_subtable_split<split_context_t> (split_context, split_points);
274   }
275 
276  private:
277 
278   struct class_info_t {
279     hb_set_t marks;
280     hb_vector_t<unsigned> child_indices;
281   };
282 
283   struct split_context_t {
284     gsubgpos_graph_context_t& c;
285     MarkBasePosFormat1* thiz;
286     unsigned this_index;
287     hb_vector_t<class_info_t> class_to_info;
288     hb_hashmap_t<unsigned, unsigned> mark_array_links;
289 
marks_forgraph::MarkBasePosFormat1::split_context_t290     hb_set_t marks_for (unsigned start, unsigned end)
291     {
292       hb_set_t marks;
293       for (unsigned klass = start; klass < end; klass++)
294       {
295         + class_to_info[klass].marks.iter ()
296         | hb_sink (marks)
297         ;
298       }
299       return marks;
300     }
301 
original_countgraph::MarkBasePosFormat1::split_context_t302     unsigned original_count ()
303     {
304       return thiz->classCount;
305     }
306 
clone_rangegraph::MarkBasePosFormat1::split_context_t307     unsigned clone_range (unsigned start, unsigned end)
308     {
309       return thiz->clone_range (*this, this->this_index, start, end);
310     }
311 
shrinkgraph::MarkBasePosFormat1::split_context_t312     bool shrink (unsigned count)
313     {
314       return thiz->shrink (*this, this->this_index, count);
315     }
316   };
317 
get_class_infograph::MarkBasePosFormat1318   hb_vector_t<class_info_t> get_class_info (gsubgpos_graph_context_t& c,
319                                             unsigned this_index)
320   {
321     hb_vector_t<class_info_t> class_to_info;
322 
323     unsigned class_count = classCount;
324     if (!class_count) return class_to_info;
325 
326     if (!class_to_info.resize (class_count))
327       return hb_vector_t<class_info_t>();
328 
329     auto mark_array = c.graph.as_table<MarkArray> (this_index, &markArray);
330     if (!mark_array) return hb_vector_t<class_info_t> ();
331     unsigned mark_count = mark_array.table->len;
332     for (unsigned mark = 0; mark < mark_count; mark++)
333     {
334       unsigned klass = (*mark_array.table)[mark].get_class ();
335       if (klass >= class_count) continue;
336       class_to_info[klass].marks.add (mark);
337     }
338 
339     for (const auto& link : mark_array.vertex->obj.real_links)
340     {
341       unsigned mark = (link.position - 2) /
342                      OT::Layout::GPOS_impl::MarkRecord::static_size;
343       unsigned klass = (*mark_array.table)[mark].get_class ();
344       if (klass >= class_count) continue;
345       class_to_info[klass].child_indices.push (link.objidx);
346     }
347 
348     unsigned base_array_id =
349         c.graph.index_for_offset (this_index, &baseArray);
350     auto& base_array_v = c.graph.vertices_[base_array_id];
351 
352     for (const auto& link : base_array_v.obj.real_links)
353     {
354       unsigned index = (link.position - 2) / OT::Offset16::static_size;
355       unsigned klass = index % class_count;
356       class_to_info[klass].child_indices.push (link.objidx);
357     }
358 
359     return class_to_info;
360   }
361 
shrinkgraph::MarkBasePosFormat1362   bool shrink (split_context_t& sc,
363                unsigned this_index,
364                unsigned count)
365   {
366     DEBUG_MSG (SUBSET_REPACK, nullptr,
367                "  Shrinking MarkBasePosFormat1 (%u) to [0, %u).",
368                this_index,
369                count);
370 
371     unsigned old_count = classCount;
372     if (count >= old_count)
373       return true;
374 
375     classCount = count;
376 
377     auto mark_coverage = sc.c.graph.as_mutable_table<Coverage> (this_index,
378                                                                 &markCoverage);
379     if (!mark_coverage) return false;
380     hb_set_t marks = sc.marks_for (0, count);
381     auto new_coverage =
382         + hb_enumerate (mark_coverage.table->iter ())
383         | hb_filter (marks, hb_first)
384         | hb_map_retains_sorting (hb_second)
385         ;
386     if (!Coverage::make_coverage (sc.c, + new_coverage,
387                                   mark_coverage.index,
388                                   4 + 2 * marks.get_population ()))
389       return false;
390 
391 
392     auto base_array = sc.c.graph.as_mutable_table<AnchorMatrix> (this_index,
393                                                                  &baseArray,
394                                                                  old_count);
395     if (!base_array || !base_array.table->shrink (sc.c,
396                                                   base_array.index,
397                                                   old_count,
398                                                   count))
399       return false;
400 
401     auto mark_array = sc.c.graph.as_mutable_table<MarkArray> (this_index,
402                                                               &markArray);
403     if (!mark_array || !mark_array.table->shrink (sc.c,
404                                                   sc.mark_array_links,
405                                                   mark_array.index,
406                                                   count))
407       return false;
408 
409     return true;
410   }
411 
412   // Create a new MarkBasePos that has all of the data for classes from [start, end).
clone_rangegraph::MarkBasePosFormat1413   unsigned clone_range (split_context_t& sc,
414                         unsigned this_index,
415                         unsigned start, unsigned end) const
416   {
417     DEBUG_MSG (SUBSET_REPACK, nullptr,
418                "  Cloning MarkBasePosFormat1 (%u) range [%u, %u).", this_index, start, end);
419 
420     graph_t& graph = sc.c.graph;
421     unsigned prime_size = OT::Layout::GPOS_impl::MarkBasePosFormat1_2<SmallTypes>::static_size;
422 
423     unsigned prime_id = sc.c.create_node (prime_size);
424     if (prime_id == (unsigned) -1) return -1;
425 
426     MarkBasePosFormat1* prime = (MarkBasePosFormat1*) graph.object (prime_id).head;
427     prime->format = this->format;
428     unsigned new_class_count = end - start;
429     prime->classCount = new_class_count;
430 
431     unsigned base_coverage_id =
432         graph.index_for_offset (sc.this_index, &baseCoverage);
433     graph.add_link (&(prime->baseCoverage), prime_id, base_coverage_id);
434     graph.duplicate (prime_id, base_coverage_id);
435 
436     auto mark_coverage = sc.c.graph.as_table<Coverage> (this_index,
437                                                         &markCoverage);
438     if (!mark_coverage) return false;
439     hb_set_t marks = sc.marks_for (start, end);
440     auto new_coverage =
441         + hb_enumerate (mark_coverage.table->iter ())
442         | hb_filter (marks, hb_first)
443         | hb_map_retains_sorting (hb_second)
444         ;
445     if (!Coverage::add_coverage (sc.c,
446                                  prime_id,
447                                  2,
448                                  + new_coverage,
449                                  marks.get_population () * 2 + 4))
450       return -1;
451 
452     auto mark_array =
453         graph.as_table <MarkArray> (sc.this_index, &markArray);
454     if (!mark_array) return -1;
455     unsigned new_mark_array =
456         mark_array.table->clone (sc.c,
457                                  mark_array.index,
458                                  sc.mark_array_links,
459                                  marks,
460                                  start);
461     graph.add_link (&(prime->markArray), prime_id, new_mark_array);
462 
463     unsigned class_count = classCount;
464     auto base_array =
465         graph.as_table<AnchorMatrix> (sc.this_index, &baseArray, class_count);
466     if (!base_array) return -1;
467     unsigned new_base_array =
468         base_array.table->clone (sc.c,
469                                  base_array.index,
470                                  start, end, this->classCount);
471     graph.add_link (&(prime->baseArray), prime_id, new_base_array);
472 
473     return prime_id;
474   }
475 };
476 
477 
478 struct MarkBasePos : public OT::Layout::GPOS_impl::MarkBasePos
479 {
split_subtablesgraph::MarkBasePos480   hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c,
481                                          unsigned parent_index,
482                                          unsigned this_index)
483   {
484     switch (u.format) {
485     case 1:
486       return ((MarkBasePosFormat1*)(&u.format1))->split_subtables (c, parent_index, this_index);
487 #ifndef HB_NO_BEYOND_64K
488     case 2: HB_FALLTHROUGH;
489       // Don't split 24bit MarkBasePos's.
490 #endif
491     default:
492       return hb_vector_t<unsigned> ();
493     }
494   }
495 
sanitizegraph::MarkBasePos496   bool sanitize (graph_t::vertex_t& vertex) const
497   {
498     int64_t vertex_len = vertex.obj.tail - vertex.obj.head;
499     if (vertex_len < u.format.get_size ()) return false;
500     hb_barrier ();
501 
502     switch (u.format) {
503     case 1:
504       return ((MarkBasePosFormat1*)(&u.format1))->sanitize (vertex);
505 #ifndef HB_NO_BEYOND_64K
506     case 2: HB_FALLTHROUGH;
507 #endif
508     default:
509       // We don't handle format 3 and 4 here.
510       return false;
511     }
512   }
513 };
514 
515 
516 }
517 
518 #endif  // GRAPH_MARKBASEPOS_GRAPH_HH
519