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