Coverage Report

Created: 2026-03-31 06:58

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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