Coverage Report

Created: 2026-07-05 06:35

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
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