/src/harfbuzz/src/graph/pairpos-graph.hh
Line | Count | Source |
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_PAIRPOS_GRAPH_HH |
28 | | #define GRAPH_PAIRPOS_GRAPH_HH |
29 | | |
30 | | #include "split-helpers.hh" |
31 | | #include "coverage-graph.hh" |
32 | | #include "classdef-graph.hh" |
33 | | #include "../OT/Layout/GPOS/PairPos.hh" |
34 | | #include "../OT/Layout/GPOS/PosLookupSubTable.hh" |
35 | | |
36 | | namespace graph { |
37 | | |
38 | | struct PairPosFormat1 : public OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes> |
39 | | { |
40 | | bool sanitize (graph_t::vertex_t& vertex) const |
41 | 3.08k | { |
42 | 3.08k | size_t vertex_len = vertex.obj.tail - vertex.obj.head; |
43 | 3.08k | unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size; |
44 | 3.08k | if (vertex_len < min_size) return false; |
45 | 2.20k | hb_barrier (); |
46 | | |
47 | 2.20k | return vertex_len >= |
48 | 2.20k | min_size + pairSet.get_size () - pairSet.len.get_size(); |
49 | 3.08k | } |
50 | | |
51 | | hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, |
52 | | unsigned this_index) |
53 | 1.00k | { |
54 | 1.00k | hb_set_t visited; |
55 | | |
56 | 1.00k | const unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); |
57 | 1.00k | const unsigned coverage_size = c.graph.vertices_[coverage_id].table_size (); |
58 | 1.00k | const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size; |
59 | | |
60 | 1.00k | unsigned partial_coverage_size = 4; |
61 | 1.00k | unsigned accumulated = base_size; |
62 | 1.00k | hb_vector_t<unsigned> split_points; |
63 | 56.8k | for (unsigned i = 0; i < pairSet.len; i++) |
64 | 55.8k | { |
65 | 55.8k | unsigned pair_set_index = pair_set_graph_index (c, this_index, i); |
66 | 55.8k | unsigned accumulated_delta = |
67 | 55.8k | c.graph.find_subgraph_size (pair_set_index, visited) + |
68 | 55.8k | SmallTypes::size; // for PairSet offset. |
69 | 55.8k | partial_coverage_size += OT::HBUINT16::static_size; |
70 | | |
71 | 55.8k | accumulated += accumulated_delta; |
72 | 55.8k | unsigned total = accumulated + hb_min (partial_coverage_size, coverage_size); |
73 | | |
74 | 55.8k | if (total >= (1 << 16)) |
75 | 6.22k | { |
76 | 6.22k | split_points.push (i); |
77 | 6.22k | accumulated = base_size + accumulated_delta; |
78 | 6.22k | partial_coverage_size = 6; |
79 | 6.22k | visited.clear (); // node sharing isn't allowed between splits. |
80 | 6.22k | } |
81 | 55.8k | } |
82 | | |
83 | 1.00k | split_context_t split_context { |
84 | 1.00k | c, |
85 | 1.00k | this, |
86 | 1.00k | this_index, |
87 | 1.00k | }; |
88 | | |
89 | 1.00k | return actuate_subtable_split<split_context_t> (split_context, split_points); |
90 | 1.00k | } |
91 | | |
92 | | private: |
93 | | |
94 | | struct split_context_t { |
95 | | gsubgpos_graph_context_t& c; |
96 | | PairPosFormat1* thiz; |
97 | | unsigned this_index; |
98 | | |
99 | | unsigned original_count () |
100 | 740 | { |
101 | 740 | return thiz->pairSet.len; |
102 | 740 | } |
103 | | |
104 | | unsigned clone_range (unsigned start, unsigned end) |
105 | 6.22k | { |
106 | 6.22k | return thiz->clone_range (this->c, this->this_index, start, end); |
107 | 6.22k | } |
108 | | |
109 | | bool shrink (unsigned count) |
110 | 740 | { |
111 | 740 | return thiz->shrink (this->c, this->this_index, count); |
112 | 740 | } |
113 | | }; |
114 | | |
115 | | bool shrink (gsubgpos_graph_context_t& c, |
116 | | unsigned this_index, |
117 | | unsigned count) |
118 | 740 | { |
119 | 740 | DEBUG_MSG (SUBSET_REPACK, nullptr, |
120 | 740 | " Shrinking PairPosFormat1 (%u) to [0, %u).", |
121 | 740 | this_index, |
122 | 740 | count); |
123 | 740 | unsigned old_count = pairSet.len; |
124 | 740 | if (count >= old_count) |
125 | 0 | return true; |
126 | | |
127 | 740 | pairSet.len = count; |
128 | 740 | c.graph.vertices_[this_index].obj.tail -= (old_count - count) * SmallTypes::size; |
129 | | |
130 | 740 | auto coverage = c.graph.as_mutable_table<Coverage> (this_index, &this->coverage); |
131 | 740 | if (!coverage) return false; |
132 | | |
133 | 740 | unsigned coverage_size = coverage.vertex->table_size (); |
134 | 740 | auto new_coverage = |
135 | 740 | + hb_zip (coverage.table->iter (), hb_range ()) |
136 | 79.7k | | hb_filter ([&] (hb_pair_t<unsigned, unsigned> p) { |
137 | 79.7k | return p.second < count; |
138 | 79.7k | }) |
139 | 740 | | hb_map_retains_sorting (hb_first) |
140 | 740 | ; |
141 | | |
142 | 740 | return Coverage::make_coverage (c, new_coverage, coverage.index, coverage_size); |
143 | 740 | } |
144 | | |
145 | | // Create a new PairPos including PairSet's from start (inclusive) to end (exclusive). |
146 | | // Returns object id of the new object. |
147 | | unsigned clone_range (gsubgpos_graph_context_t& c, |
148 | | unsigned this_index, |
149 | | unsigned start, unsigned end) const |
150 | 6.22k | { |
151 | 6.22k | DEBUG_MSG (SUBSET_REPACK, nullptr, |
152 | 6.22k | " Cloning PairPosFormat1 (%u) range [%u, %u).", this_index, start, end); |
153 | | |
154 | 6.22k | unsigned num_pair_sets = end - start; |
155 | 6.22k | unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat1_3<SmallTypes>::min_size |
156 | 6.22k | + num_pair_sets * SmallTypes::size; |
157 | | |
158 | 6.22k | unsigned pair_pos_prime_id = c.create_node (prime_size); |
159 | 6.22k | if (pair_pos_prime_id == (unsigned) -1) return -1; |
160 | | |
161 | 6.22k | PairPosFormat1* pair_pos_prime = (PairPosFormat1*) c.graph.object (pair_pos_prime_id).head; |
162 | 6.22k | pair_pos_prime->format = this->format; |
163 | 6.22k | pair_pos_prime->valueFormat[0] = this->valueFormat[0]; |
164 | 6.22k | pair_pos_prime->valueFormat[1] = this->valueFormat[1]; |
165 | 6.22k | pair_pos_prime->pairSet.len = num_pair_sets; |
166 | | |
167 | 30.0k | for (unsigned i = start; i < end; i++) |
168 | 23.8k | { |
169 | 23.8k | c.graph.move_child<> (this_index, |
170 | 23.8k | &pairSet[i], |
171 | 23.8k | pair_pos_prime_id, |
172 | 23.8k | &pair_pos_prime->pairSet[i - start]); |
173 | 23.8k | } |
174 | | |
175 | 6.22k | unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); |
176 | 6.22k | if (!Coverage::clone_coverage (c, |
177 | 6.22k | coverage_id, |
178 | 6.22k | pair_pos_prime_id, |
179 | 6.22k | 2, |
180 | 6.22k | start, end)) |
181 | 0 | return -1; |
182 | | |
183 | 6.22k | return pair_pos_prime_id; |
184 | 6.22k | } |
185 | | |
186 | | |
187 | | |
188 | | unsigned pair_set_graph_index (gsubgpos_graph_context_t& c, unsigned this_index, unsigned i) const |
189 | 55.8k | { |
190 | 55.8k | return c.graph.index_for_offset (this_index, &pairSet[i]); |
191 | 55.8k | } |
192 | | }; |
193 | | |
194 | | struct PairPosFormat2 : public OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes> |
195 | | { |
196 | | bool sanitize (graph_t::vertex_t& vertex) const |
197 | 416 | { |
198 | 416 | size_t vertex_len = vertex.table_size (); |
199 | 416 | unsigned min_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size; |
200 | 416 | if (vertex_len < min_size) return false; |
201 | 351 | hb_barrier (); |
202 | | |
203 | 351 | const unsigned class1_count = class1Count; |
204 | 351 | return vertex_len >= |
205 | 351 | min_size + class1_count * get_class1_record_size (); |
206 | 416 | } |
207 | | |
208 | | hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, |
209 | | unsigned this_index) |
210 | 316 | { |
211 | 316 | const unsigned base_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size; |
212 | 316 | const unsigned class_def_2_size = size_of (c, this_index, &classDef2); |
213 | 316 | const Coverage* coverage = get_coverage (c, this_index); |
214 | 316 | const ClassDef* class_def_1 = get_class_def_1 (c, this_index); |
215 | 316 | auto gid_and_class = |
216 | 316 | + coverage->iter () |
217 | 338k | | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { |
218 | 338k | return hb_codepoint_pair_t (gid, class_def_1->get_class (gid)); |
219 | 338k | }) |
220 | 316 | ; |
221 | 316 | class_def_size_estimator_t estimator (gid_and_class); |
222 | | |
223 | 316 | const unsigned class1_count = class1Count; |
224 | 316 | const unsigned class2_count = class2Count; |
225 | 316 | const unsigned class1_record_size = get_class1_record_size (); |
226 | | |
227 | 316 | const unsigned value_1_len = valueFormat1.get_len (); |
228 | 316 | const unsigned value_2_len = valueFormat2.get_len (); |
229 | 316 | const unsigned total_value_len = value_1_len + value_2_len; |
230 | | |
231 | 316 | unsigned accumulated = base_size; |
232 | 316 | unsigned coverage_size = 4; |
233 | 316 | unsigned class_def_1_size = 4; |
234 | 316 | unsigned max_coverage_size = coverage_size; |
235 | 316 | unsigned max_class_def_1_size = class_def_1_size; |
236 | | |
237 | 316 | hb_vector_t<unsigned> split_points; |
238 | | |
239 | 316 | hb_hashmap_t<unsigned, unsigned> device_tables = get_all_device_tables (c, this_index); |
240 | 316 | hb_vector_t<unsigned> format1_device_table_indices = valueFormat1.get_device_table_indices (); |
241 | 316 | hb_vector_t<unsigned> format2_device_table_indices = valueFormat2.get_device_table_indices (); |
242 | 316 | bool has_device_tables = bool(format1_device_table_indices) || bool(format2_device_table_indices); |
243 | | |
244 | 316 | hb_set_t visited; |
245 | 1.16M | for (unsigned i = 0; i < class1_count; i++) |
246 | 1.16M | { |
247 | 1.16M | unsigned accumulated_delta = class1_record_size; |
248 | 1.16M | class_def_1_size = estimator.add_class_def_size (i); |
249 | 1.16M | coverage_size = estimator.coverage_size (); |
250 | 1.16M | max_coverage_size = hb_max (max_coverage_size, coverage_size); |
251 | 1.16M | max_class_def_1_size = hb_max (max_class_def_1_size, class_def_1_size); |
252 | | |
253 | 1.16M | if (has_device_tables) { |
254 | 888k | for (unsigned j = 0; j < class2_count; j++) |
255 | 116 | { |
256 | 116 | unsigned value1_index = total_value_len * (class2_count * i + j); |
257 | 116 | unsigned value2_index = value1_index + value_1_len; |
258 | 116 | accumulated_delta += size_of_value_record_children (c, |
259 | 116 | device_tables, |
260 | 116 | format1_device_table_indices, |
261 | 116 | value1_index, |
262 | 116 | visited); |
263 | 116 | accumulated_delta += size_of_value_record_children (c, |
264 | 116 | device_tables, |
265 | 116 | format2_device_table_indices, |
266 | 116 | value2_index, |
267 | 116 | visited); |
268 | 116 | } |
269 | 888k | } |
270 | | |
271 | 1.16M | accumulated += accumulated_delta; |
272 | 1.16M | unsigned total = accumulated |
273 | 1.16M | + coverage_size + class_def_1_size + class_def_2_size |
274 | | // The largest object will pack last and can exceed the size limit. |
275 | 1.16M | - hb_max (hb_max (coverage_size, class_def_1_size), class_def_2_size); |
276 | 1.16M | if (total >= (1 << 16)) |
277 | 2 | { |
278 | 2 | split_points.push (i); |
279 | | // split does not include i, so add the size for i when we reset the size counters. |
280 | 2 | accumulated = base_size + accumulated_delta; |
281 | | |
282 | 2 | estimator.reset(); |
283 | 2 | class_def_1_size = estimator.add_class_def_size(i); |
284 | 2 | coverage_size = estimator.coverage_size(); |
285 | 2 | visited.clear (); // node sharing isn't allowed between splits. |
286 | 2 | } |
287 | 1.16M | } |
288 | | |
289 | 316 | split_context_t split_context { |
290 | 316 | c, |
291 | 316 | this, |
292 | 316 | this_index, |
293 | 316 | class1_record_size, |
294 | 316 | total_value_len, |
295 | 316 | value_1_len, |
296 | 316 | value_2_len, |
297 | 316 | max_coverage_size, |
298 | 316 | max_class_def_1_size, |
299 | 316 | device_tables, |
300 | 316 | format1_device_table_indices, |
301 | 316 | format2_device_table_indices |
302 | 316 | }; |
303 | | |
304 | 316 | return actuate_subtable_split<split_context_t> (split_context, split_points); |
305 | 316 | } |
306 | | private: |
307 | | |
308 | | struct split_context_t |
309 | | { |
310 | | gsubgpos_graph_context_t& c; |
311 | | PairPosFormat2* thiz; |
312 | | unsigned this_index; |
313 | | unsigned class1_record_size; |
314 | | unsigned value_record_len; |
315 | | unsigned value1_record_len; |
316 | | unsigned value2_record_len; |
317 | | unsigned max_coverage_size; |
318 | | unsigned max_class_def_size; |
319 | | |
320 | | const hb_hashmap_t<unsigned, unsigned>& device_tables; |
321 | | const hb_vector_t<unsigned>& format1_device_table_indices; |
322 | | const hb_vector_t<unsigned>& format2_device_table_indices; |
323 | | |
324 | | unsigned original_count () |
325 | 2 | { |
326 | 2 | return thiz->class1Count; |
327 | 2 | } |
328 | | |
329 | | unsigned clone_range (unsigned start, unsigned end) |
330 | 2 | { |
331 | 2 | return thiz->clone_range (*this, start, end); |
332 | 2 | } |
333 | | |
334 | | bool shrink (unsigned count) |
335 | 2 | { |
336 | 2 | return thiz->shrink (*this, count); |
337 | 2 | } |
338 | | }; |
339 | | |
340 | | size_t get_class1_record_size () const |
341 | 667 | { |
342 | 667 | const size_t class2_count = class2Count; |
343 | 667 | return |
344 | 667 | class2_count * (valueFormat1.get_size () + valueFormat2.get_size ()); |
345 | 667 | } |
346 | | |
347 | | unsigned clone_range (split_context_t& split_context, |
348 | | unsigned start, unsigned end) const |
349 | 2 | { |
350 | 2 | DEBUG_MSG (SUBSET_REPACK, nullptr, |
351 | 2 | " Cloning PairPosFormat2 (%u) range [%u, %u).", split_context.this_index, start, end); |
352 | | |
353 | 2 | graph_t& graph = split_context.c.graph; |
354 | | |
355 | 2 | unsigned num_records = end - start; |
356 | 2 | unsigned prime_size = OT::Layout::GPOS_impl::PairPosFormat2_4<SmallTypes>::min_size |
357 | 2 | + num_records * split_context.class1_record_size; |
358 | | |
359 | 2 | unsigned pair_pos_prime_id = split_context.c.create_node (prime_size); |
360 | 2 | if (pair_pos_prime_id == (unsigned) -1) return -1; |
361 | | |
362 | 2 | PairPosFormat2* pair_pos_prime = |
363 | 2 | (PairPosFormat2*) graph.object (pair_pos_prime_id).head; |
364 | 2 | pair_pos_prime->format = this->format; |
365 | 2 | pair_pos_prime->valueFormat1 = this->valueFormat1; |
366 | 2 | pair_pos_prime->valueFormat2 = this->valueFormat2; |
367 | 2 | pair_pos_prime->class1Count = num_records; |
368 | 2 | pair_pos_prime->class2Count = this->class2Count; |
369 | 2 | clone_class1_records (split_context, |
370 | 2 | pair_pos_prime_id, |
371 | 2 | start, |
372 | 2 | end); |
373 | | |
374 | 2 | unsigned coverage_id = |
375 | 2 | graph.index_for_offset (split_context.this_index, &coverage); |
376 | 2 | unsigned class_def_1_id = |
377 | 2 | graph.index_for_offset (split_context.this_index, &classDef1); |
378 | 2 | auto& coverage_v = graph.vertices_[coverage_id]; |
379 | 2 | auto& class_def_1_v = graph.vertices_[class_def_1_id]; |
380 | 2 | Coverage* coverage_table = (Coverage*) coverage_v.obj.head; |
381 | 2 | ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; |
382 | 2 | if (!coverage_table |
383 | 2 | || !coverage_table->sanitize (coverage_v) |
384 | 2 | || !class_def_1_table |
385 | 2 | || !class_def_1_table->sanitize (class_def_1_v)) |
386 | 0 | return -1; |
387 | | |
388 | 2 | auto klass_map = |
389 | 2 | + coverage_table->iter () |
390 | 12 | | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { |
391 | 12 | return hb_codepoint_pair_t (gid, class_def_1_table->get_class (gid)); |
392 | 12 | }) |
393 | 6 | | hb_filter ([&] (hb_codepoint_t klass) { |
394 | 6 | return klass >= start && klass < end; |
395 | 6 | }, hb_second) |
396 | 6 | | hb_map_retains_sorting ([&] (hb_codepoint_pair_t gid_and_class) { |
397 | | // Classes must be from 0...N so subtract start |
398 | 6 | return hb_codepoint_pair_t (gid_and_class.first, gid_and_class.second - start); |
399 | 6 | }) |
400 | 2 | ; |
401 | | |
402 | 2 | if (!Coverage::add_coverage (split_context.c, |
403 | 2 | pair_pos_prime_id, |
404 | 2 | 2, |
405 | 2 | + klass_map | hb_map_retains_sorting (hb_first), |
406 | 2 | split_context.max_coverage_size)) |
407 | 0 | return -1; |
408 | | |
409 | | // classDef1 |
410 | 2 | if (!ClassDef::add_class_def (split_context.c, |
411 | 2 | pair_pos_prime_id, |
412 | 2 | 8, |
413 | 2 | + klass_map, |
414 | 2 | split_context.max_class_def_size)) |
415 | 0 | return -1; |
416 | | |
417 | | // classDef2 |
418 | 2 | unsigned class_def_2_id = |
419 | 2 | graph.index_for_offset (split_context.this_index, &classDef2); |
420 | 2 | auto* class_def_link = graph.vertices_[pair_pos_prime_id].obj.real_links.push (); |
421 | 2 | class_def_link->width = SmallTypes::size; |
422 | 2 | class_def_link->objidx = class_def_2_id; |
423 | 2 | class_def_link->position = 10; |
424 | 2 | graph.vertices_[class_def_2_id].add_parent (pair_pos_prime_id, false); |
425 | | |
426 | 2 | return pair_pos_prime_id; |
427 | 2 | } |
428 | | |
429 | | void clone_class1_records (split_context_t& split_context, |
430 | | unsigned pair_pos_prime_id, |
431 | | unsigned start, unsigned end) const |
432 | 2 | { |
433 | 2 | PairPosFormat2* pair_pos_prime = |
434 | 2 | (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; |
435 | | |
436 | 2 | char* start_addr = ((char*)&values[0]) + start * split_context.class1_record_size; |
437 | 2 | unsigned num_records = end - start; |
438 | 2 | hb_memcpy (&pair_pos_prime->values[0], |
439 | 2 | start_addr, |
440 | 2 | num_records * split_context.class1_record_size); |
441 | | |
442 | 2 | if (!split_context.format1_device_table_indices |
443 | 0 | && !split_context.format2_device_table_indices) |
444 | | // No device tables to move over. |
445 | 0 | return; |
446 | | |
447 | 2 | unsigned class2_count = class2Count; |
448 | 4 | for (unsigned i = start; i < end; i++) |
449 | 2 | { |
450 | 34 | for (unsigned j = 0; j < class2_count; j++) |
451 | 32 | { |
452 | 32 | unsigned value1_index = split_context.value_record_len * (class2_count * i + j); |
453 | 32 | unsigned value2_index = value1_index + split_context.value1_record_len; |
454 | | |
455 | 32 | unsigned new_value1_index = split_context.value_record_len * (class2_count * (i - start) + j); |
456 | 32 | unsigned new_value2_index = new_value1_index + split_context.value1_record_len; |
457 | | |
458 | 32 | transfer_device_tables (split_context, |
459 | 32 | pair_pos_prime_id, |
460 | 32 | split_context.format1_device_table_indices, |
461 | 32 | value1_index, |
462 | 32 | new_value1_index); |
463 | | |
464 | 32 | transfer_device_tables (split_context, |
465 | 32 | pair_pos_prime_id, |
466 | 32 | split_context.format2_device_table_indices, |
467 | 32 | value2_index, |
468 | 32 | new_value2_index); |
469 | 32 | } |
470 | 2 | } |
471 | 2 | } |
472 | | |
473 | | void transfer_device_tables (split_context_t& split_context, |
474 | | unsigned pair_pos_prime_id, |
475 | | const hb_vector_t<unsigned>& device_table_indices, |
476 | | unsigned old_value_record_index, |
477 | | unsigned new_value_record_index) const |
478 | 64 | { |
479 | 64 | PairPosFormat2* pair_pos_prime = |
480 | 64 | (PairPosFormat2*) split_context.c.graph.object (pair_pos_prime_id).head; |
481 | | |
482 | 64 | for (unsigned i : device_table_indices) |
483 | 32 | { |
484 | 32 | OT::Offset16* record = (OT::Offset16*) &values[old_value_record_index + i]; |
485 | 32 | unsigned record_position = ((char*) record) - ((char*) this); |
486 | 32 | if (!split_context.device_tables.has (record_position)) continue; |
487 | | |
488 | 16 | split_context.c.graph.move_child ( |
489 | 16 | split_context.this_index, |
490 | 16 | record, |
491 | 16 | pair_pos_prime_id, |
492 | 16 | (OT::Offset16*) &pair_pos_prime->values[new_value_record_index + i]); |
493 | 16 | } |
494 | 64 | } |
495 | | |
496 | | bool shrink (split_context_t& split_context, |
497 | | unsigned count) |
498 | 2 | { |
499 | 2 | DEBUG_MSG (SUBSET_REPACK, nullptr, |
500 | 2 | " Shrinking PairPosFormat2 (%u) to [0, %u).", |
501 | 2 | split_context.this_index, |
502 | 2 | count); |
503 | 2 | unsigned old_count = class1Count; |
504 | 2 | if (count >= old_count) |
505 | 0 | return true; |
506 | | |
507 | 2 | graph_t& graph = split_context.c.graph; |
508 | 2 | class1Count = count; |
509 | 2 | graph.vertices_[split_context.this_index].obj.tail -= |
510 | 2 | (old_count - count) * split_context.class1_record_size; |
511 | | |
512 | 2 | auto coverage = |
513 | 2 | graph.as_mutable_table<Coverage> (split_context.this_index, &this->coverage); |
514 | 2 | if (!coverage) return false; |
515 | | |
516 | 2 | auto class_def_1 = |
517 | 2 | graph.as_mutable_table<ClassDef> (split_context.this_index, &classDef1); |
518 | 2 | if (!class_def_1) return false; |
519 | | |
520 | 2 | auto klass_map = |
521 | 2 | + coverage.table->iter () |
522 | 61 | | hb_map_retains_sorting ([&] (hb_codepoint_t gid) { |
523 | 61 | return hb_codepoint_pair_t (gid, class_def_1.table->get_class (gid)); |
524 | 61 | }) |
525 | 32 | | hb_filter ([&] (hb_codepoint_t klass) { |
526 | 32 | return klass < count; |
527 | 32 | }, hb_second) |
528 | 2 | ; |
529 | | |
530 | 2 | auto new_coverage = + klass_map | hb_map_retains_sorting (hb_first); |
531 | 2 | if (!Coverage::make_coverage (split_context.c, |
532 | 2 | + new_coverage, |
533 | 2 | coverage.index, |
534 | | // existing ranges my not be kept, worst case size is a format 1 |
535 | | // coverage table. |
536 | 2 | 4 + new_coverage.len() * 2)) |
537 | 0 | return false; |
538 | | |
539 | 2 | return ClassDef::make_class_def (split_context.c, |
540 | 2 | + klass_map, |
541 | 2 | class_def_1.index, |
542 | 2 | class_def_1.vertex->table_size ()); |
543 | 2 | } |
544 | | |
545 | | hb_hashmap_t<unsigned, unsigned> |
546 | | get_all_device_tables (gsubgpos_graph_context_t& c, |
547 | | unsigned this_index) const |
548 | 316 | { |
549 | 316 | const auto& v = c.graph.vertices_[this_index]; |
550 | 316 | return v.position_to_index_map (); |
551 | 316 | } |
552 | | |
553 | | const Coverage* get_coverage (gsubgpos_graph_context_t& c, |
554 | | unsigned this_index) const |
555 | 316 | { |
556 | 316 | unsigned coverage_id = c.graph.index_for_offset (this_index, &coverage); |
557 | 316 | auto& coverage_v = c.graph.vertices_[coverage_id]; |
558 | | |
559 | 316 | Coverage* coverage_table = (Coverage*) coverage_v.obj.head; |
560 | 316 | if (!coverage_table || !coverage_table->sanitize (coverage_v)) |
561 | 48 | return &Null(Coverage); |
562 | 268 | return coverage_table; |
563 | 316 | } |
564 | | |
565 | | const ClassDef* get_class_def_1 (gsubgpos_graph_context_t& c, |
566 | | unsigned this_index) const |
567 | 316 | { |
568 | 316 | unsigned class_def_1_id = c.graph.index_for_offset (this_index, &classDef1); |
569 | 316 | auto& class_def_1_v = c.graph.vertices_[class_def_1_id]; |
570 | | |
571 | 316 | ClassDef* class_def_1_table = (ClassDef*) class_def_1_v.obj.head; |
572 | 316 | if (!class_def_1_table || !class_def_1_table->sanitize (class_def_1_v)) |
573 | 97 | return &Null(ClassDef); |
574 | 219 | return class_def_1_table; |
575 | 316 | } |
576 | | |
577 | | unsigned size_of_value_record_children (gsubgpos_graph_context_t& c, |
578 | | const hb_hashmap_t<unsigned, unsigned>& device_tables, |
579 | | const hb_vector_t<unsigned> device_table_indices, |
580 | | unsigned value_record_index, |
581 | | hb_set_t& visited) |
582 | 232 | { |
583 | 232 | unsigned size = 0; |
584 | 232 | for (unsigned i : device_table_indices) |
585 | 124 | { |
586 | 124 | OT::Layout::GPOS_impl::Value* record = &values[value_record_index + i]; |
587 | 124 | unsigned record_position = ((char*) record) - ((char*) this); |
588 | 124 | unsigned* obj_idx; |
589 | 124 | if (!device_tables.has (record_position, &obj_idx)) continue; |
590 | 29 | size += c.graph.find_subgraph_size (*obj_idx, visited); |
591 | 29 | } |
592 | 232 | return size; |
593 | 232 | } |
594 | | |
595 | | unsigned size_of (gsubgpos_graph_context_t& c, |
596 | | unsigned this_index, |
597 | | const void* offset) const |
598 | 316 | { |
599 | 316 | const unsigned id = c.graph.index_for_offset (this_index, offset); |
600 | 316 | return c.graph.vertices_[id].table_size (); |
601 | 316 | } |
602 | | }; |
603 | | |
604 | | struct PairPos : public OT::Layout::GPOS_impl::PairPos |
605 | | { |
606 | | hb_vector_t<unsigned> split_subtables (gsubgpos_graph_context_t& c, |
607 | | unsigned this_index) |
608 | 1.32k | { |
609 | 1.32k | switch (u.format.v) { |
610 | 1.00k | case 1: |
611 | 1.00k | return ((PairPosFormat1*)(&u.format1))->split_subtables (c, this_index); |
612 | 316 | case 2: |
613 | 316 | return ((PairPosFormat2*)(&u.format2))->split_subtables (c, this_index); |
614 | 0 | #ifndef HB_NO_BEYOND_64K |
615 | 0 | case 3: HB_FALLTHROUGH; |
616 | 0 | case 4: HB_FALLTHROUGH; |
617 | | // Don't split 24bit PairPos's. |
618 | 0 | #endif |
619 | 0 | default: |
620 | 0 | return hb_vector_t<unsigned> (); |
621 | 1.32k | } |
622 | 1.32k | } |
623 | | |
624 | | bool sanitize (graph_t::vertex_t& vertex) const |
625 | 3.75k | { |
626 | 3.75k | size_t vertex_len = vertex.obj.tail - vertex.obj.head; |
627 | 3.75k | if (vertex_len < u.format.v.get_size ()) return false; |
628 | 3.74k | hb_barrier (); |
629 | | |
630 | 3.74k | switch (u.format.v) { |
631 | 3.08k | case 1: |
632 | 3.08k | return ((PairPosFormat1*)(&u.format1))->sanitize (vertex); |
633 | 416 | case 2: |
634 | 416 | return ((PairPosFormat2*)(&u.format2))->sanitize (vertex); |
635 | 0 | #ifndef HB_NO_BEYOND_64K |
636 | 131 | case 3: HB_FALLTHROUGH; |
637 | 131 | case 4: HB_FALLTHROUGH; |
638 | 131 | #endif |
639 | 243 | default: |
640 | | // We don't handle format 3 and 4 here. |
641 | 243 | return false; |
642 | 3.74k | } |
643 | 3.74k | } |
644 | | }; |
645 | | |
646 | | } |
647 | | |
648 | | #endif // GRAPH_PAIRPOS_GRAPH_HH |