Coverage Report

Created: 2026-08-13 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ots/src/gsub.cc
Line
Count
Source
1
// Copyright (c) 2011-2017 The OTS Authors. All rights reserved.
2
// Use of this source code is governed by a BSD-style license that can be
3
// found in the LICENSE file.
4
5
#include "gsub.h"
6
7
#include <limits>
8
#include <vector>
9
10
#include "layout.h"
11
#include "maxp.h"
12
13
// GSUB - The Glyph Substitution Table
14
// http://www.microsoft.com/typography/otspec/gsub.htm
15
16
#define TABLE_NAME "GSUB"
17
18
namespace {
19
20
enum GSUB_TYPE {
21
  GSUB_TYPE_SINGLE = 1,
22
  GSUB_TYPE_MULTIPLE = 2,
23
  GSUB_TYPE_ALTERNATE = 3,
24
  GSUB_TYPE_LIGATURE = 4,
25
  GSUB_TYPE_CONTEXT = 5,
26
  GSUB_TYPE_CHANGING_CONTEXT = 6,
27
  GSUB_TYPE_EXTENSION_SUBSTITUTION = 7,
28
  GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE = 8,
29
  GSUB_TYPE_RESERVED = 9
30
};
31
32
bool ParseSequenceTable(const ots::Font *font,
33
                        const uint8_t *data, const size_t length,
34
12.4k
                        const uint16_t num_glyphs) {
35
12.4k
  ots::Buffer subtable(data, length);
36
37
12.4k
  uint16_t glyph_count = 0;
38
12.4k
  if (!subtable.ReadU16(&glyph_count)) {
39
6
    return OTS_FAILURE_MSG("Failed to read glyph count in sequence table");
40
6
  }
41
40.2k
  for (unsigned i = 0; i < glyph_count; ++i) {
42
27.9k
    uint16_t substitute = 0;
43
27.9k
    if (!subtable.ReadU16(&substitute)) {
44
8
      return OTS_FAILURE_MSG("Failed to read substitution %d in sequence table", i);
45
8
    }
46
27.8k
    if (substitute >= num_glyphs) {
47
35
      return OTS_FAILURE_MSG("Bad substitution (%d) %d > %d", i, substitute, num_glyphs);
48
35
    }
49
27.8k
  }
50
51
12.3k
  return true;
52
12.4k
}
53
54
bool ParseAlternateSetTable(const ots::Font *font,
55
                            const uint8_t *data, const size_t length,
56
1.29k
                            const uint16_t num_glyphs) {
57
1.29k
  ots::Buffer subtable(data, length);
58
59
1.29k
  uint16_t glyph_count = 0;
60
1.29k
  if (!subtable.ReadU16(&glyph_count)) {
61
0
    return OTS_FAILURE_MSG("Failed to read alternate set header");
62
0
  }
63
3.68k
  for (unsigned i = 0; i < glyph_count; ++i) {
64
2.41k
    uint16_t alternate = 0;
65
2.41k
    if (!subtable.ReadU16(&alternate)) {
66
6
      return OTS_FAILURE_MSG("Can't read alternate %d", i);
67
6
    }
68
2.41k
    if (alternate >= num_glyphs) {
69
17
      return OTS_FAILURE_MSG("Too large alternate: %u", alternate);
70
17
    }
71
2.41k
  }
72
1.26k
  return true;
73
1.29k
}
74
75
bool ParseLigatureTable(const ots::Font *font,
76
                        const uint8_t *data, const size_t length,
77
16.8k
                        const uint16_t num_glyphs) {
78
16.8k
  ots::Buffer subtable(data, length);
79
80
16.8k
  uint16_t lig_glyph = 0;
81
16.8k
  uint16_t comp_count = 0;
82
83
16.8k
  if (!subtable.ReadU16(&lig_glyph) ||
84
16.8k
      !subtable.ReadU16(&comp_count)) {
85
26
    return OTS_FAILURE_MSG("Failed to read ligature table header");
86
26
  }
87
88
16.8k
  if (lig_glyph >= num_glyphs) {
89
17
    return OTS_FAILURE_MSG("too large lig_glyph: %u", lig_glyph);
90
17
  }
91
16.8k
  if (comp_count == 0) {
92
36
    return OTS_FAILURE_MSG("Component count cannot be 0");
93
36
  }
94
45.1k
  for (unsigned i = 0; i < comp_count - static_cast<unsigned>(1); ++i) {
95
28.3k
    uint16_t component = 0;
96
28.3k
    if (!subtable.ReadU16(&component)) {
97
12
      return OTS_FAILURE_MSG("Can't read ligature component %d", i);
98
12
    }
99
28.3k
    if (component >= num_glyphs) {
100
28
      return OTS_FAILURE_MSG("Bad ligature component %d of %d", i, component);
101
28
    }
102
28.3k
  }
103
104
16.7k
  return true;
105
16.8k
}
106
107
bool ParseLigatureSetTable(const ots::Font *font,
108
                           const uint8_t *data, const size_t length,
109
7.86k
                           const uint16_t num_glyphs) {
110
7.86k
  ots::Buffer subtable(data, length);
111
112
7.86k
  uint16_t ligature_count = 0;
113
114
7.86k
  if (!subtable.ReadU16(&ligature_count)) {
115
6
    return OTS_FAILURE_MSG("Can't read ligature count in ligature set");
116
6
  }
117
118
7.85k
  const unsigned ligature_end = static_cast<unsigned>(2) + ligature_count * 2;
119
7.85k
  if (ligature_end > std::numeric_limits<uint16_t>::max()) {
120
39
    return OTS_FAILURE_MSG("Bad end of ligature %d in ligature set", ligature_end);
121
39
  }
122
24.5k
  for (unsigned i = 0; i < ligature_count; ++i) {
123
16.9k
    uint16_t offset_ligature = 0;
124
16.9k
    if (!subtable.ReadU16(&offset_ligature)) {
125
20
      return OTS_FAILURE_MSG("Failed to read ligature offset %d", i);
126
20
    }
127
16.9k
    if (offset_ligature < ligature_end || offset_ligature >= length) {
128
66
      return OTS_FAILURE_MSG("Bad ligature offset %d for ligature %d", offset_ligature, i);
129
66
    }
130
16.8k
    if (!ParseLigatureTable(font, data + offset_ligature, length - offset_ligature,
131
16.8k
                            num_glyphs)) {
132
119
      return OTS_FAILURE_MSG("Failed to parse ligature %d", i);
133
119
    }
134
16.8k
  }
135
136
7.61k
  return true;
137
7.81k
}
138
139
}  // namespace
140
141
namespace ots {
142
143
// Lookup Type 1:
144
// Single Substitution Subtable
145
bool OpenTypeGSUB::ParseSingleSubstitution(const uint8_t *data,
146
1.56k
                                           const size_t length) {
147
1.56k
  Font* font = GetFont();
148
1.56k
  Buffer subtable(data, length);
149
150
1.56k
  uint16_t format = 0;
151
1.56k
  uint16_t offset_coverage = 0;
152
153
1.56k
  if (!subtable.ReadU16(&format) ||
154
1.56k
      !subtable.ReadU16(&offset_coverage)) {
155
6
    return Error("Failed to read single subst table header");
156
6
  }
157
158
1.55k
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
159
1.55k
      font->GetTypedTable(OTS_TAG_MAXP));
160
1.55k
  if (!maxp) {
161
0
    return Error("Required maxp table missing");
162
0
  }
163
1.55k
  const uint16_t num_glyphs = maxp->num_glyphs;
164
1.55k
  if (format == 1) {
165
    // Parse SingleSubstFormat1
166
868
    int16_t delta_glyph_id = 0;
167
868
    if (!subtable.ReadS16(&delta_glyph_id)) {
168
15
      return Error("Failed to read glyph shift from format 1 single subst table");
169
15
    }
170
853
    if (std::abs(delta_glyph_id) >= num_glyphs) {
171
21
      return Error("bad glyph shift of %d in format 1 single subst table", delta_glyph_id);
172
21
    }
173
853
  } else if (format == 2) {
174
    // Parse SingleSubstFormat2
175
652
    uint16_t glyph_count = 0;
176
652
    if (!subtable.ReadU16(&glyph_count)) {
177
10
      return Error("Failed to read glyph cound in format 2 single subst table");
178
10
    }
179
642
    if (glyph_count > num_glyphs) {
180
32
      return Error("Bad glyph count %d > %d in format 2 single subst table", glyph_count, num_glyphs);
181
32
    }
182
12.0k
    for (unsigned i = 0; i < glyph_count; ++i) {
183
11.3k
      uint16_t substitute = 0;
184
11.3k
      if (!subtable.ReadU16(&substitute)) {
185
2
        return Error("Failed to read substitution %d in format 2 single subst table", i);
186
2
      }
187
11.3k
      if (substitute >= num_glyphs) {
188
7
        return Error("too large substitute: %u", substitute);
189
7
      }
190
11.3k
    }
191
610
  } else {
192
37
    return Error("Bad single subst table format %d", format);
193
37
  }
194
195
1.43k
  if (offset_coverage < subtable.offset() || offset_coverage >= length) {
196
42
    return Error("Bad coverage offset %x", offset_coverage);
197
42
  }
198
1.39k
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
199
1.39k
                               length - offset_coverage, num_glyphs)) {
200
45
    return Error("Failed to parse coverage table");
201
45
  }
202
203
1.34k
  return true;
204
1.39k
}
205
206
// Lookup Type 2:
207
// Multiple Substitution Subtable
208
bool OpenTypeGSUB::ParseMutipleSubstitution(const uint8_t *data,
209
818
                                            const size_t length) {
210
818
  Font* font = GetFont();
211
818
  Buffer subtable(data, length);
212
213
818
  uint16_t format = 0;
214
818
  uint16_t offset_coverage = 0;
215
818
  uint16_t sequence_count = 0;
216
217
818
  if (!subtable.ReadU16(&format) ||
218
814
      !subtable.ReadU16(&offset_coverage) ||
219
805
      !subtable.ReadU16(&sequence_count)) {
220
21
    return Error("Can't read header of multiple subst table");
221
21
  }
222
223
797
  if (format != 1) {
224
38
    return Error("Bad multiple subst table format %d", format);
225
38
  }
226
227
759
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
228
759
      font->GetTypedTable(OTS_TAG_MAXP));
229
759
  if (!maxp) {
230
0
    return Error("Required maxp table missing");
231
0
  }
232
759
  const uint16_t num_glyphs = maxp->num_glyphs;
233
759
  const unsigned sequence_end = static_cast<unsigned>(6) +
234
759
      sequence_count * 2;
235
759
  if (sequence_end > std::numeric_limits<uint16_t>::max()) {
236
26
    return Error("Bad sequence end %d, in multiple subst", sequence_end);
237
26
  }
238
13.1k
  for (unsigned i = 0; i < sequence_count; ++i) {
239
12.4k
    uint16_t offset_sequence = 0;
240
12.4k
    if (!subtable.ReadU16(&offset_sequence)) {
241
1
      return Error("Failed to read sequence offset for sequence %d", i);
242
1
    }
243
12.4k
    if (offset_sequence < sequence_end || offset_sequence >= length) {
244
54
      return Error("Bad sequence offset %d for sequence %d", offset_sequence, i);
245
54
    }
246
12.4k
    if (!ParseSequenceTable(font, data + offset_sequence, length - offset_sequence,
247
12.4k
                            num_glyphs)) {
248
49
      return Error("Failed to parse sequence table %d", i);
249
49
    }
250
12.4k
  }
251
252
629
  if (offset_coverage < sequence_end || offset_coverage >= length) {
253
41
    return Error("Bad coverage offset %d", offset_coverage);
254
41
  }
255
588
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
256
588
                               length - offset_coverage, num_glyphs)) {
257
40
    return Error("Failed to parse coverage table");
258
40
  }
259
260
548
  return true;
261
588
}
262
263
// Lookup Type 3:
264
// Alternate Substitution Subtable
265
bool OpenTypeGSUB::ParseAlternateSubstitution(const uint8_t *data,
266
414
                                              const size_t length) {
267
414
  Font* font = GetFont();
268
414
  Buffer subtable(data, length);
269
270
414
  uint16_t format = 0;
271
414
  uint16_t offset_coverage = 0;
272
414
  uint16_t alternate_set_count = 0;
273
274
414
  if (!subtable.ReadU16(&format) ||
275
413
      !subtable.ReadU16(&offset_coverage) ||
276
409
      !subtable.ReadU16(&alternate_set_count)) {
277
19
    return Error("Can't read alternate subst header");
278
19
  }
279
280
395
  if (format != 1) {
281
15
    return Error("Bad alternate subst table format %d", format);
282
15
  }
283
284
380
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
285
380
      font->GetTypedTable(OTS_TAG_MAXP));
286
380
  if (!maxp) {
287
0
    return Error("Required maxp table missing");
288
0
  }
289
380
  const uint16_t num_glyphs = maxp->num_glyphs;
290
380
  const unsigned alternate_set_end = static_cast<unsigned>(6) +
291
380
      alternate_set_count * 2;
292
380
  if (alternate_set_end > std::numeric_limits<uint16_t>::max()) {
293
19
    return Error("Bad end of alternate set %d", alternate_set_end);
294
19
  }
295
1.62k
  for (unsigned i = 0; i < alternate_set_count; ++i) {
296
1.34k
    uint16_t offset_alternate_set = 0;
297
1.34k
    if (!subtable.ReadU16(&offset_alternate_set)) {
298
4
      return Error("Can't read alternate set offset for set %d", i);
299
4
    }
300
1.34k
    if (offset_alternate_set < alternate_set_end ||
301
1.31k
        offset_alternate_set >= length) {
302
51
      return Error("Bad alternate set offset %d for set %d", offset_alternate_set, i);
303
51
    }
304
1.29k
    if (!ParseAlternateSetTable(font, data + offset_alternate_set,
305
1.29k
                                length - offset_alternate_set,
306
1.29k
                                num_glyphs)) {
307
23
      return Error("Failed to parse alternate set");
308
23
    }
309
1.29k
  }
310
311
283
  if (offset_coverage < alternate_set_end || offset_coverage >= length) {
312
34
    return Error("Bad coverage offset %d", offset_coverage);
313
34
  }
314
249
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
315
249
                               length - offset_coverage, num_glyphs)) {
316
19
    return Error("Failed to parse coverage table");
317
19
  }
318
319
230
  return true;
320
249
}
321
322
// Lookup Type 4:
323
// Ligature Substitution Subtable
324
bool OpenTypeGSUB::ParseLigatureSubstitution(const uint8_t *data,
325
1.39k
                                             const size_t length) {
326
1.39k
  Font* font = GetFont();
327
1.39k
  Buffer subtable(data, length);
328
329
1.39k
  uint16_t format = 0;
330
1.39k
  uint16_t offset_coverage = 0;
331
1.39k
  uint16_t lig_set_count = 0;
332
333
1.39k
  if (!subtable.ReadU16(&format) ||
334
1.39k
      !subtable.ReadU16(&offset_coverage) ||
335
1.38k
      !subtable.ReadU16(&lig_set_count)) {
336
23
    return Error("Failed to read ligature substitution header");
337
23
  }
338
339
1.37k
  if (format != 1) {
340
20
    return Error("Bad ligature substitution table format %d", format);
341
20
  }
342
343
1.35k
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
344
1.35k
      font->GetTypedTable(OTS_TAG_MAXP));
345
1.35k
  if (!maxp) {
346
0
    return Error("Required maxp table missing");
347
0
  }
348
1.35k
  const uint16_t num_glyphs = maxp->num_glyphs;
349
1.35k
  const unsigned ligature_set_end = static_cast<unsigned>(6) +
350
1.35k
      lig_set_count * 2;
351
1.35k
  if (ligature_set_end > std::numeric_limits<uint16_t>::max()) {
352
3
    return Error("Bad end of ligature set %d in ligature substitution table", ligature_set_end);
353
3
  }
354
8.95k
  for (unsigned i = 0; i < lig_set_count; ++i) {
355
7.88k
    uint16_t offset_ligature_set = 0;
356
7.88k
    if (!subtable.ReadU16(&offset_ligature_set)) {
357
1
      return Error("Can't read ligature set offset %d", i);
358
1
    }
359
7.88k
    if (offset_ligature_set < ligature_set_end ||
360
7.87k
        offset_ligature_set >= length) {
361
25
      return Error("Bad ligature set offset %d for set %d", offset_ligature_set, i);
362
25
    }
363
7.86k
    if (!ParseLigatureSetTable(font, data + offset_ligature_set,
364
7.86k
                               length - offset_ligature_set, num_glyphs)) {
365
250
      return Error("Failed to parse ligature set %d", i);
366
250
    }
367
7.86k
  }
368
369
1.07k
  if (offset_coverage < ligature_set_end || offset_coverage >= length) {
370
30
    return Error("Bad coverage offset %d", offset_coverage);
371
30
  }
372
1.04k
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
373
1.04k
                               length - offset_coverage, num_glyphs)) {
374
23
    return Error("Failed to parse coverage table");
375
23
  }
376
377
1.02k
  return true;
378
1.04k
}
379
380
// Lookup Type 5:
381
// Contextual Substitution Subtable
382
// OpenTypeLayoutTable::ParseContextSubtable()
383
384
// Lookup Type 6:
385
// Chaining Contextual Substitution Subtable
386
// OpenTypeLayoutTable::ParseChainingContextSubtable
387
388
// Lookup Type 7:
389
// Extension Substition
390
// OpenTypeLayoutTable::ParseExtensionSubtable
391
392
// Lookup Type 8:
393
// Reverse Chaining Contexual Single Substitution Subtable
394
bool OpenTypeGSUB::ParseReverseChainingContextSingleSubstitution(const uint8_t *data,
395
499
                                                                 const size_t length) {
396
499
  Font* font = GetFont();
397
499
  Buffer subtable(data, length);
398
399
499
  uint16_t format = 0;
400
499
  uint16_t offset_coverage = 0;
401
402
499
  if (!subtable.ReadU16(&format) ||
403
498
      !subtable.ReadU16(&offset_coverage)) {
404
3
    return Error("Failed to read reverse chaining header");
405
3
  }
406
407
496
  OpenTypeMAXP *maxp = static_cast<OpenTypeMAXP*>(
408
496
      font->GetTypedTable(OTS_TAG_MAXP));
409
496
  if (!maxp) {
410
0
    return Error("Required maxp table missing");
411
0
  }
412
496
  const uint16_t num_glyphs = maxp->num_glyphs;
413
414
496
  uint16_t backtrack_glyph_count = 0;
415
496
  if (!subtable.ReadU16(&backtrack_glyph_count)) {
416
7
    return Error("Failed to read backtrack glyph count in reverse chaining table");
417
7
  }
418
489
  std::vector<uint16_t> offsets_backtrack;
419
489
  offsets_backtrack.reserve(backtrack_glyph_count);
420
516k
  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
421
515k
    uint16_t offset = 0;
422
515k
    if (!subtable.ReadU16(&offset)) {
423
22
      return Error("Failed to read backtrack offset %d", i);
424
22
    }
425
515k
    offsets_backtrack.push_back(offset);
426
515k
  }
427
428
467
  uint16_t lookahead_glyph_count = 0;
429
467
  if (!subtable.ReadU16(&lookahead_glyph_count)) {
430
10
    return Error("Failed to read look ahead glyph count");
431
10
  }
432
457
  std::vector<uint16_t> offsets_lookahead;
433
457
  offsets_lookahead.reserve(lookahead_glyph_count);
434
192k
  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
435
192k
    uint16_t offset = 0;
436
192k
    if (!subtable.ReadU16(&offset)) {
437
24
      return Error("Can't read look ahead offset %d", i);
438
24
    }
439
192k
    offsets_lookahead.push_back(offset);
440
192k
  }
441
442
433
  uint16_t glyph_count = 0;
443
433
  if (!subtable.ReadU16(&glyph_count)) {
444
18
    return Error("Can't read glyph count in reverse chaining table");
445
18
  }
446
854
  for (unsigned i = 0; i < glyph_count; ++i) {
447
492
    uint16_t substitute = 0;
448
492
    if (!subtable.ReadU16(&substitute)) {
449
7
      return Error("Failed to read substitution %d reverse chaining table", i);
450
7
    }
451
485
    if (substitute >= num_glyphs) {
452
46
      return Error("Bad substitute glyph %d in reverse chaining table substitution %d", substitute, i);
453
46
    }
454
485
  }
455
456
362
  const unsigned substitute_end = static_cast<unsigned>(10) +
457
362
      (backtrack_glyph_count + lookahead_glyph_count + glyph_count) * 2;
458
362
  if (substitute_end > std::numeric_limits<uint16_t>::max()) {
459
7
    return Error("Bad substitute end offset in reverse chaining table");
460
7
  }
461
462
355
  if (offset_coverage < substitute_end || offset_coverage >= length) {
463
97
    return Error("Bad coverage offset %d in reverse chaining table", offset_coverage);
464
97
  }
465
258
  if (!ots::ParseCoverageTable(font, data + offset_coverage,
466
258
                               length - offset_coverage, num_glyphs)) {
467
29
    return Error("Failed to parse coverage table in reverse chaining table");
468
29
  }
469
470
292
  for (unsigned i = 0; i < backtrack_glyph_count; ++i) {
471
125
    if (offsets_backtrack[i] < substitute_end ||
472
106
        offsets_backtrack[i] >= length) {
473
45
      return Error("Bad backtrack offset %d for backtrack %d in reverse chaining table", offsets_backtrack[i], i);
474
45
    }
475
80
    if (!ots::ParseCoverageTable(font, data + offsets_backtrack[i],
476
80
                                 length - offsets_backtrack[i], num_glyphs)) {
477
17
      return Error("Failed to parse coverage table for backtrack %d in reverse chaining table", i);
478
17
    }
479
80
  }
480
481
267
  for (unsigned i = 0; i < lookahead_glyph_count; ++i) {
482
163
    if (offsets_lookahead[i] < substitute_end ||
483
142
        offsets_lookahead[i] >= length) {
484
59
      return Error("Bad lookahead offset %d for lookahead %d in reverse chaining table", offsets_lookahead[i], i);
485
59
    }
486
104
    if (!ots::ParseCoverageTable(font, data + offsets_lookahead[i],
487
104
                                 length - offsets_lookahead[i], num_glyphs)) {
488
4
      return Error("Failed to parse lookahead coverage table %d in reverse chaining table", i);
489
4
    }
490
104
  }
491
492
104
  return true;
493
167
}
494
495
bool OpenTypeGSUB::ValidLookupSubtableType(const uint16_t lookup_type,
496
6.94k
                                           bool extension) const {
497
6.94k
  if (extension && lookup_type == GSUB_TYPE_EXTENSION_SUBSTITUTION)
498
10
    return false;
499
6.93k
  return lookup_type >= GSUB_TYPE_SINGLE && lookup_type < GSUB_TYPE_RESERVED;
500
6.94k
}
501
502
bool OpenTypeGSUB::ParseLookupSubtable(const uint8_t *data, const size_t length,
503
9.04k
                                       const uint16_t lookup_type) {
504
9.04k
  switch (lookup_type) {
505
1.56k
    case GSUB_TYPE_SINGLE:
506
1.56k
      return ParseSingleSubstitution(data, length);
507
818
    case GSUB_TYPE_MULTIPLE:
508
818
      return ParseMutipleSubstitution(data, length);
509
414
    case GSUB_TYPE_ALTERNATE:
510
414
      return ParseAlternateSubstitution(data, length);
511
1.39k
    case GSUB_TYPE_LIGATURE:
512
1.39k
      return ParseLigatureSubstitution(data, length);
513
1.02k
    case GSUB_TYPE_CONTEXT:
514
1.02k
      return ParseContextSubtable(data, length);
515
2.73k
    case GSUB_TYPE_CHANGING_CONTEXT:
516
2.73k
      return ParseChainingContextSubtable(data, length);
517
590
    case GSUB_TYPE_EXTENSION_SUBSTITUTION:
518
590
      return ParseExtensionSubtable(data, length);
519
499
    case GSUB_TYPE_REVERSE_CHAINING_CONTEXT_SINGLE:
520
499
      return ParseReverseChainingContextSingleSubstitution(data, length);
521
9.04k
  }
522
0
  return false;
523
9.04k
}
524
525
}  // namespace ots
526
527
#undef TABLE_NAME