Coverage Report

Created: 2026-06-09 06:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/harfbuzz/src/graph/gsubgpos-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
#include "graph.hh"
28
#include "../hb-ot-layout-gsubgpos.hh"
29
#include "../OT/Layout/GSUB/ExtensionSubst.hh"
30
#include "../OT/Layout/GSUB/SubstLookupSubTable.hh"
31
#include "gsubgpos-context.hh"
32
#include "pairpos-graph.hh"
33
#include "markbasepos-graph.hh"
34
#include "ligature-graph.hh"
35
36
#ifndef GRAPH_GSUBGPOS_GRAPH_HH
37
#define GRAPH_GSUBGPOS_GRAPH_HH
38
39
namespace graph {
40
41
struct Lookup;
42
43
template<typename T>
44
struct ExtensionFormat1 : public OT::ExtensionFormat1<T>
45
{
46
  void reset(unsigned type)
47
50.4k
  {
48
50.4k
    this->format = 1;
49
50.4k
    this->extensionLookupType = type;
50
50.4k
    this->extensionOffset = 0;
51
50.4k
  }
52
53
  bool sanitize (graph_t::vertex_t& vertex) const
54
24.8k
  {
55
24.8k
    size_t vertex_len = vertex.obj.tail - vertex.obj.head;
56
24.8k
    return vertex_len >= OT::ExtensionFormat1<T>::static_size;
57
24.8k
  }
58
59
  unsigned get_lookup_type () const
60
24.7k
  {
61
24.7k
    return this->extensionLookupType;
62
24.7k
  }
63
64
  unsigned get_subtable_index (graph_t& graph, unsigned this_index) const
65
24.7k
  {
66
24.7k
    return graph.index_for_offset (this_index, &this->extensionOffset);
67
24.7k
  }
68
};
69
70
struct Lookup : public OT::Lookup
71
{
72
  unsigned number_of_subtables () const
73
101k
  {
74
101k
    return subTable.len;
75
101k
  }
76
77
  bool sanitize (graph_t::vertex_t& vertex) const
78
120k
  {
79
120k
    size_t vertex_len = vertex.obj.tail - vertex.obj.head;
80
120k
    if (vertex_len < OT::Lookup::min_size) return false;
81
120k
    hb_barrier ();
82
120k
    return vertex_len >= this->get_size ();
83
120k
  }
84
85
  bool is_extension (hb_tag_t table_tag) const
86
219k
  {
87
219k
    return lookupType == extension_type (table_tag);
88
219k
  }
89
90
  bool use_mark_filtering_set () const
91
727
  {
92
727
    unsigned flag = lookupFlag;
93
727
    return flag & 0x0010u;
94
727
  }
95
96
  bool make_extension (gsubgpos_graph_context_t& c,
97
                       unsigned this_index)
98
14.6k
  {
99
14.6k
    unsigned type = lookupType;
100
14.6k
    unsigned ext_type = extension_type (c.table_tag);
101
14.6k
    if (!ext_type || is_extension (c.table_tag))
102
0
    {
103
      // NOOP
104
0
      return true;
105
0
    }
106
107
14.6k
    DEBUG_MSG (SUBSET_REPACK, nullptr,
108
14.6k
               "Promoting lookup type %u (obj %u) to extension.",
109
14.6k
               type,
110
14.6k
               this_index);
111
112
1.37M
    for (unsigned i = 0; i < subTable.len; i++)
113
1.35M
    {
114
1.35M
      unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
115
1.35M
      if (!make_subtable_extension (c,
116
1.35M
                                    this_index,
117
1.35M
                                    subtable_index))
118
56
        return false;
119
1.35M
    }
120
121
14.5k
    lookupType = ext_type;
122
14.5k
    return true;
123
14.6k
  }
124
125
  bool split_subtables_if_needed (gsubgpos_graph_context_t& c,
126
                                  unsigned this_index)
127
101k
  {
128
101k
    unsigned type = lookupType;
129
101k
    bool is_ext = is_extension (c.table_tag);
130
131
101k
    if (c.table_tag != HB_OT_TAG_GPOS && c.table_tag != HB_OT_TAG_GSUB)
132
0
      return true;
133
134
101k
    if (!is_ext && !is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c))
135
71.1k
      return true;
136
137
30.8k
    hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>> all_new_subtables;
138
820k
    for (unsigned i = 0; i < subTable.len; i++)
139
789k
    {
140
789k
      unsigned subtable_index = c.graph.index_for_offset (this_index, &subTable[i]);
141
789k
      if (is_ext) {
142
132k
        unsigned ext_subtable_index = subtable_index;
143
132k
        ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension =
144
132k
            (ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*)
145
132k
            c.graph.object (ext_subtable_index).head;
146
132k
        if (!extension || !extension->sanitize (c.graph.vertices_[ext_subtable_index]))
147
107k
          continue;
148
149
24.7k
        subtable_index = extension->get_subtable_index (c.graph, ext_subtable_index);
150
24.7k
        type = extension->get_lookup_type ();
151
24.7k
        if (!is_supported_gpos_type(type, c) && !is_supported_gsub_type(type, c))
152
24.3k
          continue;
153
24.7k
      }
154
155
657k
      hb_vector_t<unsigned>* split_result;
156
657k
      if (c.split_subtables.has (subtable_index, &split_result))
157
507k
      {
158
507k
        if (split_result->length == 0)
159
506k
          continue;
160
336
        all_new_subtables.push (hb_pair(i, *split_result));
161
336
      }
162
150k
      else
163
150k
      {
164
150k
        hb_vector_t<unsigned> new_sub_tables;
165
166
150k
        if (c.table_tag == HB_OT_TAG_GPOS) {
167
116k
          switch (type)
168
116k
          {
169
33.8k
          case 2:
170
33.8k
            new_sub_tables = split_subtable<PairPos> (c, subtable_index); break;
171
82.8k
          case 4:
172
82.8k
            new_sub_tables = split_subtable<MarkBasePos> (c, subtable_index); break;
173
0
          default:
174
0
            break;
175
116k
          }
176
116k
        } else if (c.table_tag == HB_OT_TAG_GSUB) {
177
34.0k
          switch (type)
178
34.0k
          {
179
34.0k
          case 4:
180
34.0k
            new_sub_tables = split_subtable<graph::LigatureSubst> (c, subtable_index); break;
181
0
          default:
182
0
            break;
183
34.0k
          }
184
34.0k
        }
185
186
150k
        if (new_sub_tables.in_error ()) return false;
187
188
150k
        c.split_subtables.set (subtable_index, new_sub_tables);
189
150k
        if (new_sub_tables)
190
875
          all_new_subtables.push (hb_pair (i, std::move (new_sub_tables)));
191
150k
      }
192
657k
    }
193
194
30.8k
    if (all_new_subtables) {
195
727
      return add_sub_tables (c, this_index, type, all_new_subtables);
196
727
    }
197
198
30.0k
    return true;
199
30.8k
  }
200
201
  template<typename T>
202
  hb_vector_t<unsigned> split_subtable (gsubgpos_graph_context_t& c,
203
                                        unsigned objidx)
204
150k
  {
205
150k
    T* sub_table = (T*) c.graph.object (objidx).head;
206
150k
    if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx]))
207
148k
      return hb_vector_t<unsigned> ();
208
209
2.65k
    return sub_table->split_subtables (c, objidx);
210
150k
  }
hb_vector_t<unsigned int, false> graph::Lookup::split_subtable<graph::PairPos>(graph::gsubgpos_graph_context_t&, unsigned int)
Line
Count
Source
204
33.8k
  {
205
33.8k
    T* sub_table = (T*) c.graph.object (objidx).head;
206
33.8k
    if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx]))
207
32.7k
      return hb_vector_t<unsigned> ();
208
209
1.13k
    return sub_table->split_subtables (c, objidx);
210
33.8k
  }
hb_vector_t<unsigned int, false> graph::Lookup::split_subtable<graph::MarkBasePos>(graph::gsubgpos_graph_context_t&, unsigned int)
Line
Count
Source
204
82.8k
  {
205
82.8k
    T* sub_table = (T*) c.graph.object (objidx).head;
206
82.8k
    if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx]))
207
82.5k
      return hb_vector_t<unsigned> ();
208
209
354
    return sub_table->split_subtables (c, objidx);
210
82.8k
  }
hb_vector_t<unsigned int, false> graph::Lookup::split_subtable<graph::LigatureSubst>(graph::gsubgpos_graph_context_t&, unsigned int)
Line
Count
Source
204
34.0k
  {
205
34.0k
    T* sub_table = (T*) c.graph.object (objidx).head;
206
34.0k
    if (!sub_table || !sub_table->sanitize (c.graph.vertices_[objidx]))
207
32.8k
      return hb_vector_t<unsigned> ();
208
209
1.17k
    return sub_table->split_subtables (c, objidx);
210
34.0k
  }
211
212
  bool add_sub_tables (gsubgpos_graph_context_t& c,
213
                       unsigned this_index,
214
                       unsigned type,
215
                       const hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
216
727
  {
217
727
    bool is_ext = is_extension (c.table_tag);
218
727
    auto* v = &c.graph.vertices_[this_index];
219
727
    fix_existing_subtable_links (c, this_index, subtable_ids);
220
221
727
    unsigned new_subtable_count = 0;
222
727
    for (const auto& p : subtable_ids)
223
1.21k
      new_subtable_count += p.second.length;
224
225
727
    size_t new_size = v->table_size ()
226
727
                      + new_subtable_count * OT::Offset16::static_size;
227
727
    char* buffer = (char*) hb_calloc (1, new_size);
228
727
    if (!buffer) return false;
229
727
    if (!c.add_buffer (buffer))
230
0
    {
231
0
      hb_free (buffer);
232
0
     return false;
233
0
    }
234
727
    hb_memcpy (buffer, v->obj.head, v->table_size());
235
236
727
    if (use_mark_filtering_set ())
237
0
      hb_memcpy (buffer + new_size - 2, v->obj.tail - 2, 2);
238
239
727
    v->obj.head = buffer;
240
727
    v->obj.tail = buffer + new_size;
241
242
727
    Lookup* new_lookup = (Lookup*) buffer;
243
244
727
    unsigned shift = 0;
245
727
    new_lookup->subTable.len = subTable.len + new_subtable_count;
246
727
    for (const auto& p : subtable_ids)
247
1.21k
    {
248
1.21k
      unsigned offset_index = p.first + shift + 1;
249
1.21k
      shift += p.second.length;
250
251
1.21k
      for (unsigned subtable_id : p.second)
252
28.7k
      {
253
28.7k
        if (is_ext)
254
8
        {
255
8
          unsigned ext_id = create_extension_subtable (c, subtable_id, type);
256
8
          c.graph.vertices_[subtable_id].add_parent (ext_id, false);
257
8
          subtable_id = ext_id;
258
          // the reference to v may have changed on adding a node, so reassign it.
259
8
          v = &c.graph.vertices_[this_index];
260
8
        }
261
262
28.7k
        auto* link = v->obj.real_links.push ();
263
28.7k
        link->width = 2;
264
28.7k
        link->objidx = subtable_id;
265
28.7k
        link->position = (char*) &new_lookup->subTable[offset_index++] -
266
28.7k
                         (char*) new_lookup;
267
28.7k
        c.graph.vertices_[subtable_id].add_parent (this_index, false);
268
28.7k
      }
269
1.21k
    }
270
271
    // Repacker sort order depends on link order, which we've messed up so resort it.
272
727
    v->obj.real_links.qsort ();
273
274
    // The head location of the lookup has changed, invalidating the lookups map entry
275
    // in the context. Update the map.
276
727
    c.lookups.set (this_index, new_lookup);
277
727
    return true;
278
727
  }
279
280
  void fix_existing_subtable_links (gsubgpos_graph_context_t& c,
281
                                    unsigned this_index,
282
                                    const hb_vector_t<hb_pair_t<unsigned, hb_vector_t<unsigned>>>& subtable_ids)
283
727
  {
284
727
    auto& v = c.graph.vertices_[this_index];
285
727
    unsigned shift = 0;
286
727
    for (const auto& p : subtable_ids)
287
1.21k
    {
288
1.21k
      unsigned insert_index = p.first + shift;
289
1.21k
      unsigned pos_offset = p.second.length * OT::Offset16::static_size;
290
1.21k
      unsigned insert_offset = Lookup::min_size + insert_index * OT::Offset16::static_size;
291
1.21k
      shift += p.second.length;
292
293
1.21k
      for (auto& l : v.obj.all_links_writer ())
294
3.16k
      {
295
3.16k
        if (l.position > insert_offset) l.position += pos_offset;
296
3.16k
      }
297
1.21k
    }
298
727
  }
299
300
  unsigned create_extension_subtable (gsubgpos_graph_context_t& c,
301
                                      unsigned subtable_index,
302
                                      unsigned type)
303
50.4k
  {
304
50.4k
    unsigned extension_size = OT::ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>::static_size;
305
306
50.4k
    unsigned ext_index = c.create_node (extension_size);
307
50.4k
    if (ext_index == (unsigned) -1)
308
56
      return -1;
309
310
50.4k
    auto& ext_vertex = c.graph.vertices_[ext_index];
311
50.4k
    ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>* extension =
312
50.4k
        (ExtensionFormat1<OT::Layout::GSUB_impl::ExtensionSubst>*) ext_vertex.obj.head;
313
50.4k
    extension->reset (type);
314
315
    // Make extension point at the subtable.
316
50.4k
    auto* l = ext_vertex.obj.real_links.push ();
317
318
50.4k
    l->width = 4;
319
50.4k
    l->objidx = subtable_index;
320
50.4k
    l->position = 4;
321
322
50.4k
    return ext_index;
323
50.4k
  }
324
325
  bool make_subtable_extension (gsubgpos_graph_context_t& c,
326
                                unsigned lookup_index,
327
                                unsigned subtable_index)
328
1.35M
  {
329
1.35M
    unsigned type = lookupType;
330
1.35M
    unsigned ext_index = -1;
331
1.35M
    unsigned* existing_ext_index = nullptr;
332
1.35M
    if (c.subtable_to_extension.has(subtable_index, &existing_ext_index)) {
333
1.30M
      ext_index = *existing_ext_index;
334
1.30M
    } else {
335
50.4k
      ext_index = create_extension_subtable(c, subtable_index, type);
336
50.4k
      c.subtable_to_extension.set(subtable_index, ext_index);
337
50.4k
    }
338
339
1.35M
    if (ext_index == (unsigned) -1)
340
56
      return false;
341
342
1.35M
    auto& subtable_vertex = c.graph.vertices_[subtable_index];
343
1.35M
    auto& lookup_vertex = c.graph.vertices_[lookup_index];
344
1.35M
    for (auto& l : lookup_vertex.obj.real_links.writer ())
345
6.26M
    {
346
6.26M
      if (l.objidx == subtable_index) {
347
        // Change lookup to point at the extension.
348
69.1k
        l.objidx = ext_index;
349
69.1k
        if (existing_ext_index)
350
3
          subtable_vertex.remove_parent(lookup_index);
351
69.1k
      }
352
6.26M
    }
353
354
    // Make extension point at the subtable.
355
1.35M
    auto& ext_vertex = c.graph.vertices_[ext_index];
356
1.35M
    ext_vertex.add_parent (lookup_index, false);
357
1.35M
    if (!existing_ext_index)
358
50.4k
      subtable_vertex.remap_parent (lookup_index, ext_index);
359
360
1.35M
    return true;
361
1.35M
  }
362
363
 private:
364
96.7k
  bool is_supported_gsub_type(unsigned type, gsubgpos_graph_context_t& c) const {
365
96.7k
    return (c.table_tag == HB_OT_TAG_GSUB) && (
366
37.5k
      type == OT::Layout::GSUB_impl::SubstLookupSubTable::Type::Ligature
367
37.5k
    );
368
96.7k
  }
369
370
101k
  bool is_supported_gpos_type(unsigned type, gsubgpos_graph_context_t& c) const {
371
101k
   return (c.table_tag == HB_OT_TAG_GPOS) && (
372
63.8k
      type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::Pair ||
373
60.1k
      type == OT::Layout::GPOS_impl::PosLookupSubTable::Type::MarkBase
374
63.8k
    );
375
101k
  }
376
377
  unsigned extension_type (hb_tag_t table_tag) const
378
233k
  {
379
233k
    switch (table_tag)
380
233k
    {
381
150k
    case HB_OT_TAG_GPOS: return 9;
382
83.6k
    case HB_OT_TAG_GSUB: return 7;
383
0
    default: return 0;
384
233k
    }
385
233k
  }
386
};
387
388
template <typename T>
389
struct LookupList : public OT::LookupList<T>
390
{
391
  bool sanitize (const graph_t::vertex_t& vertex) const
392
4.59k
  {
393
4.59k
    size_t vertex_len = vertex.obj.tail - vertex.obj.head;
394
4.59k
    if (vertex_len < OT::LookupList<T>::min_size) return false;
395
4.58k
    hb_barrier ();
396
4.58k
    return vertex_len >= OT::LookupList<T>::item_size * this->len;
397
4.59k
  }
graph::LookupList<OT::Layout::SmallTypes>::sanitize(graph::graph_t::vertex_t const&) const
Line
Count
Source
392
4.59k
  {
393
4.59k
    size_t vertex_len = vertex.obj.tail - vertex.obj.head;
394
4.59k
    if (vertex_len < OT::LookupList<T>::min_size) return false;
395
4.58k
    hb_barrier ();
396
4.58k
    return vertex_len >= OT::LookupList<T>::item_size * this->len;
397
4.59k
  }
Unexecuted instantiation: graph::LookupList<OT::Layout::MediumTypes>::sanitize(graph::graph_t::vertex_t const&) const
398
};
399
400
struct GSTAR : public OT::GSUBGPOS
401
{
402
  static GSTAR* graph_to_gstar (graph_t& graph)
403
5.29k
  {
404
5.29k
    const auto& r = graph.root ();
405
406
5.29k
    GSTAR* gstar = (GSTAR*) r.obj.head;
407
5.29k
    if (!gstar || !gstar->sanitize (r))
408
205
      return nullptr;
409
5.09k
    hb_barrier ();
410
411
5.09k
    return gstar;
412
5.29k
  }
413
414
  const void* get_lookup_list_field_offset () const
415
9.75k
  {
416
9.75k
    switch (u.version.major) {
417
9.31k
    case 1: return u.version1.get_lookup_list_offset ();
418
0
#ifndef HB_NO_BEYOND_64K
419
16
    case 2: return u.version2.get_lookup_list_offset ();
420
0
#endif
421
424
    default: return 0;
422
9.75k
    }
423
9.75k
  }
424
425
  bool sanitize (const graph_t::vertex_t& vertex)
426
5.29k
  {
427
5.29k
    size_t len = vertex.obj.tail - vertex.obj.head;
428
5.29k
    if (len < OT::GSUBGPOS::min_size) return false;
429
5.09k
    hb_barrier ();
430
5.09k
    return len >= get_size ();
431
5.29k
  }
432
433
  void find_lookups (graph_t& graph,
434
                     hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */)
435
5.09k
  {
436
5.09k
    switch (u.version.major) {
437
4.65k
      case 1: find_lookups<SmallTypes> (graph, lookups); break;
438
0
#ifndef HB_NO_BEYOND_64K
439
8
      case 2: find_lookups<MediumTypes> (graph, lookups); break;
440
5.09k
#endif
441
5.09k
    }
442
5.09k
  }
443
444
  unsigned get_lookup_list_index (graph_t& graph)
445
9.75k
  {
446
9.75k
    return graph.index_for_offset (graph.root_idx (),
447
9.75k
                                   get_lookup_list_field_offset());
448
9.75k
  }
449
450
  template<typename Types>
451
  void find_lookups (graph_t& graph,
452
                     hb_hashmap_t<unsigned, Lookup*>& lookups /* OUT */)
453
4.66k
  {
454
4.66k
    unsigned lookup_list_idx = get_lookup_list_index (graph);
455
4.66k
    const LookupList<Types>* lookupList =
456
4.66k
        (const LookupList<Types>*) graph.object (lookup_list_idx).head;
457
4.66k
    if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx]))
458
117
      return;
459
460
258k
    for (unsigned i = 0; i < lookupList->len; i++)
461
254k
    {
462
254k
      unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i]));
463
254k
      Lookup* lookup = (Lookup*) graph.object (lookup_idx).head;
464
254k
      if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue;
465
107k
      lookups.set (lookup_idx, lookup);
466
107k
    }
467
4.54k
  }
void graph::GSTAR::find_lookups<OT::Layout::SmallTypes>(graph::graph_t&, hb_hashmap_t<unsigned int, graph::Lookup*, false>&)
Line
Count
Source
453
4.65k
  {
454
4.65k
    unsigned lookup_list_idx = get_lookup_list_index (graph);
455
4.65k
    const LookupList<Types>* lookupList =
456
4.65k
        (const LookupList<Types>*) graph.object (lookup_list_idx).head;
457
4.65k
    if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx]))
458
109
      return;
459
460
258k
    for (unsigned i = 0; i < lookupList->len; i++)
461
254k
    {
462
254k
      unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i]));
463
254k
      Lookup* lookup = (Lookup*) graph.object (lookup_idx).head;
464
254k
      if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue;
465
107k
      lookups.set (lookup_idx, lookup);
466
107k
    }
467
4.54k
  }
void graph::GSTAR::find_lookups<OT::Layout::MediumTypes>(graph::graph_t&, hb_hashmap_t<unsigned int, graph::Lookup*, false>&)
Line
Count
Source
453
8
  {
454
8
    unsigned lookup_list_idx = get_lookup_list_index (graph);
455
8
    const LookupList<Types>* lookupList =
456
8
        (const LookupList<Types>*) graph.object (lookup_list_idx).head;
457
8
    if (!lookupList || !lookupList->sanitize (graph.vertices_[lookup_list_idx]))
458
8
      return;
459
460
0
    for (unsigned i = 0; i < lookupList->len; i++)
461
0
    {
462
0
      unsigned lookup_idx = graph.index_for_offset (lookup_list_idx, &(lookupList->arrayZ[i]));
463
0
      Lookup* lookup = (Lookup*) graph.object (lookup_idx).head;
464
0
      if (!lookup || !lookup->sanitize (graph.vertices_[lookup_idx])) continue;
465
0
      lookups.set (lookup_idx, lookup);
466
0
    }
467
0
  }
468
};
469
470
471
472
473
}
474
475
#endif  /* GRAPH_GSUBGPOS_GRAPH_HH */