hb-ot-layout-gsubgpos-private.hh revision c605bbbb6d4b2a98b1f40ca818760088d991f7d1
1/*
2 * Copyright © 2007,2008,2009,2010  Red Hat, Inc.
3 * Copyright © 2010  Google, Inc.
4 *
5 *  This is part of HarfBuzz, a text shaping library.
6 *
7 * Permission is hereby granted, without written agreement and without
8 * license or royalty fees, to use, copy, modify, and distribute this
9 * software and its documentation for any purpose, provided that the
10 * above copyright notice and the following two paragraphs appear in
11 * all copies of this software.
12 *
13 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
14 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
15 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
16 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
17 * DAMAGE.
18 *
19 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
20 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
21 * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
22 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
23 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
24 *
25 * Red Hat Author(s): Behdad Esfahbod
26 * Google Author(s): Behdad Esfahbod
27 */
28
29#ifndef HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
30#define HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH
31
32#include "hb-buffer-private.hh"
33#include "hb-ot-layout-gdef-private.hh"
34
35
36/* buffer var allocations */
37#define lig_id() var2.u8[2] /* unique ligature id */
38#define lig_comp() var2.u8[3] /* component number in the ligature (0 = base) */
39
40static inline uint8_t allocate_lig_id (hb_buffer_t *buffer) {
41  uint8_t lig_id = buffer->next_serial ();
42  if (unlikely (!lig_id)) lig_id = buffer->next_serial (); /* in case of overflow */
43  return lig_id;
44}
45
46
47
48#ifndef HB_DEBUG_APPLY
49#define HB_DEBUG_APPLY (HB_DEBUG+0)
50#endif
51
52#define TRACE_APPLY() \
53	hb_auto_trace_t<HB_DEBUG_APPLY> trace (&c->debug_depth, "APPLY", this, NULL, HB_FUNC);
54
55
56
57struct hb_apply_context_t
58{
59  unsigned int debug_depth;
60  hb_font_t *font;
61  hb_face_t *face;
62  hb_buffer_t *buffer;
63  hb_direction_t direction;
64  hb_mask_t lookup_mask;
65  unsigned int context_length;
66  unsigned int nesting_level_left;
67  unsigned int lookup_props;
68  unsigned int property; /* propety of first glyph */
69
70
71  inline void replace_glyph (hb_codepoint_t glyph_index) const
72  {
73    clear_property ();
74    buffer->replace_glyph (glyph_index);
75  }
76  inline void replace_glyphs_be16 (unsigned int num_in,
77				   unsigned int num_out,
78				   const uint16_t *glyph_data_be) const
79  {
80    clear_property ();
81    buffer->replace_glyphs_be16 (num_in, num_out, glyph_data_be);
82  }
83
84  inline void guess_glyph_class (unsigned int klass)
85  {
86    /* XXX if ! has gdef */
87    buffer->info[buffer->idx].props_cache() = klass;
88  }
89
90  private:
91  inline void clear_property (void) const
92  {
93    /* XXX if has gdef */
94    buffer->info[buffer->idx].props_cache() = 0;
95  }
96};
97
98
99
100typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, const void *data);
101typedef bool (*apply_lookup_func_t) (hb_apply_context_t *c, unsigned int lookup_index);
102
103struct ContextFuncs
104{
105  match_func_t match;
106  apply_lookup_func_t apply;
107};
108
109
110static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, const void *data HB_UNUSED)
111{
112  return glyph_id == value;
113}
114
115static inline bool match_class (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
116{
117  const ClassDef &class_def = *reinterpret_cast<const ClassDef *>(data);
118  return class_def.get_class (glyph_id) == value;
119}
120
121static inline bool match_coverage (hb_codepoint_t glyph_id, const USHORT &value, const void *data)
122{
123  const OffsetTo<Coverage> &coverage = (const OffsetTo<Coverage>&)value;
124  return (data+coverage) (glyph_id) != NOT_COVERED;
125}
126
127
128static inline bool match_input (hb_apply_context_t *c,
129				unsigned int count, /* Including the first glyph (not matched) */
130				const USHORT input[], /* Array of input values--start with second glyph */
131				match_func_t match_func,
132				const void *match_data,
133				unsigned int *context_length_out)
134{
135  unsigned int i, j;
136  unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
137  if (unlikely (c->buffer->idx + count > end))
138    return false;
139
140  for (i = 1, j = c->buffer->idx + 1; i < count; i++, j++)
141  {
142    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
143    {
144      if (unlikely (j + count - i == end))
145	return false;
146      j++;
147    }
148
149    if (likely (!match_func (c->buffer->info[j].codepoint, input[i - 1], match_data)))
150      return false;
151  }
152
153  *context_length_out = j - c->buffer->idx;
154
155  return true;
156}
157
158static inline bool match_backtrack (hb_apply_context_t *c,
159				    unsigned int count,
160				    const USHORT backtrack[],
161				    match_func_t match_func,
162				    const void *match_data)
163{
164  if (unlikely (c->buffer->backtrack_len () < count))
165    return false;
166
167  for (unsigned int i = 0, j = c->buffer->backtrack_len () - 1; i < count; i++, j--)
168  {
169    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->out_info[j], c->lookup_props, NULL))
170    {
171      if (unlikely (j + 1 == count - i))
172	return false;
173      j--;
174    }
175
176    if (likely (!match_func (c->buffer->out_info[j].codepoint, backtrack[i], match_data)))
177      return false;
178  }
179
180  return true;
181}
182
183static inline bool match_lookahead (hb_apply_context_t *c,
184				    unsigned int count,
185				    const USHORT lookahead[],
186				    match_func_t match_func,
187				    const void *match_data,
188				    unsigned int offset)
189{
190  unsigned int i, j;
191  unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
192  if (unlikely (c->buffer->idx + offset + count > end))
193    return false;
194
195  for (i = 0, j = c->buffer->idx + offset; i < count; i++, j++)
196  {
197    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[j], c->lookup_props, NULL))
198    {
199      if (unlikely (j + count - i == end))
200	return false;
201      j++;
202    }
203
204    if (likely (!match_func (c->buffer->info[j].codepoint, lookahead[i], match_data)))
205      return false;
206  }
207
208  return true;
209}
210
211
212
213struct LookupRecord
214{
215  inline bool sanitize (hb_sanitize_context_t *c) {
216    TRACE_SANITIZE ();
217    return c->check_struct (this);
218  }
219
220  USHORT	sequenceIndex;		/* Index into current glyph
221					 * sequence--first glyph = 0 */
222  USHORT	lookupListIndex;	/* Lookup to apply to that
223					 * position--zero--based */
224  public:
225  DEFINE_SIZE_STATIC (4);
226};
227
228
229
230static inline bool apply_lookup (hb_apply_context_t *c,
231				 unsigned int count, /* Including the first glyph */
232				 unsigned int lookupCount,
233				 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
234				 apply_lookup_func_t apply_func)
235{
236  unsigned int end = MIN (c->buffer->len, c->buffer->idx + c->context_length);
237  if (unlikely (count == 0 || c->buffer->idx + count > end))
238    return false;
239
240  /* TODO We don't support lookupRecord arrays that are not increasing:
241   *      Should be easy for in_place ones at least. */
242
243  /* Note: If sublookup is reverse, it will underflow after the first loop
244   * and we jump out of it.  Not entirely disastrous.  So we don't check
245   * for reverse lookup here.
246   */
247  for (unsigned int i = 0; i < count; /* NOP */)
248  {
249    while (_hb_ot_layout_skip_mark (c->face, &c->buffer->info[c->buffer->idx], c->lookup_props, NULL))
250    {
251      if (unlikely (c->buffer->idx == end))
252	return true;
253      /* No lookup applied for this index */
254      c->buffer->next_glyph ();
255    }
256
257    if (lookupCount && i == lookupRecord->sequenceIndex)
258    {
259      unsigned int old_pos = c->buffer->idx;
260
261      /* Apply a lookup */
262      bool done = apply_func (c, lookupRecord->lookupListIndex);
263
264      lookupRecord++;
265      lookupCount--;
266      /* Err, this is wrong if the lookup jumped over some glyphs */
267      i += c->buffer->idx - old_pos;
268      if (unlikely (c->buffer->idx == end))
269	return true;
270
271      if (!done)
272	goto not_applied;
273    }
274    else
275    {
276    not_applied:
277      /* No lookup applied for this index */
278      c->buffer->next_glyph ();
279      i++;
280    }
281  }
282
283  return true;
284}
285
286
287
288/* Contextual lookups */
289
290struct ContextLookupContext
291{
292  ContextFuncs funcs;
293  const void *match_data;
294};
295
296static inline bool context_lookup (hb_apply_context_t *c,
297				   unsigned int inputCount, /* Including the first glyph (not matched) */
298				   const USHORT input[], /* Array of input values--start with second glyph */
299				   unsigned int lookupCount,
300				   const LookupRecord lookupRecord[],
301				   ContextLookupContext &lookup_context)
302{
303  hb_apply_context_t new_context = *c;
304  return match_input (c,
305		      inputCount, input,
306		      lookup_context.funcs.match, lookup_context.match_data,
307		      &new_context.context_length)
308      && apply_lookup (&new_context,
309		       inputCount,
310		       lookupCount, lookupRecord,
311		       lookup_context.funcs.apply);
312}
313
314struct Rule
315{
316  friend struct RuleSet;
317
318  private:
319  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
320  {
321    TRACE_APPLY ();
322    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (input, input[0].static_size * (inputCount ? inputCount - 1 : 0));
323    return context_lookup (c,
324			   inputCount, input,
325			   lookupCount, lookupRecord,
326			   lookup_context);
327  }
328
329  public:
330  inline bool sanitize (hb_sanitize_context_t *c) {
331    TRACE_SANITIZE ();
332    return inputCount.sanitize (c)
333	&& lookupCount.sanitize (c)
334	&& c->check_range (input,
335				 input[0].static_size * inputCount
336				 + lookupRecordX[0].static_size * lookupCount);
337  }
338
339  private:
340  USHORT	inputCount;		/* Total number of glyphs in input
341					 * glyph sequence--includes the  first
342					 * glyph */
343  USHORT	lookupCount;		/* Number of LookupRecords */
344  USHORT	input[VAR];		/* Array of match inputs--start with
345					 * second glyph */
346  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
347					 * design order */
348  public:
349  DEFINE_SIZE_ARRAY2 (4, input, lookupRecordX);
350};
351
352struct RuleSet
353{
354  inline bool apply (hb_apply_context_t *c, ContextLookupContext &lookup_context) const
355  {
356    TRACE_APPLY ();
357    unsigned int num_rules = rule.len;
358    for (unsigned int i = 0; i < num_rules; i++)
359    {
360      if ((this+rule[i]).apply (c, lookup_context))
361        return true;
362    }
363
364    return false;
365  }
366
367  inline bool sanitize (hb_sanitize_context_t *c) {
368    TRACE_SANITIZE ();
369    return rule.sanitize (c, this);
370  }
371
372  private:
373  OffsetArrayOf<Rule>
374		rule;			/* Array of Rule tables
375					 * ordered by preference */
376  public:
377  DEFINE_SIZE_ARRAY (2, rule);
378};
379
380
381struct ContextFormat1
382{
383  friend struct Context;
384
385  private:
386  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
387  {
388    TRACE_APPLY ();
389    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
390    if (likely (index == NOT_COVERED))
391      return false;
392
393    const RuleSet &rule_set = this+ruleSet[index];
394    struct ContextLookupContext lookup_context = {
395      {match_glyph, apply_func},
396      NULL
397    };
398    return rule_set.apply (c, lookup_context);
399  }
400
401  inline bool sanitize (hb_sanitize_context_t *c) {
402    TRACE_SANITIZE ();
403    return coverage.sanitize (c, this)
404	&& ruleSet.sanitize (c, this);
405  }
406
407  private:
408  USHORT	format;			/* Format identifier--format = 1 */
409  OffsetTo<Coverage>
410		coverage;		/* Offset to Coverage table--from
411					 * beginning of table */
412  OffsetArrayOf<RuleSet>
413		ruleSet;		/* Array of RuleSet tables
414					 * ordered by Coverage Index */
415  public:
416  DEFINE_SIZE_ARRAY (6, ruleSet);
417};
418
419
420struct ContextFormat2
421{
422  friend struct Context;
423
424  private:
425  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
426  {
427    TRACE_APPLY ();
428    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
429    if (likely (index == NOT_COVERED))
430      return false;
431
432    const ClassDef &class_def = this+classDef;
433    index = class_def (c->buffer->info[c->buffer->idx].codepoint);
434    const RuleSet &rule_set = this+ruleSet[index];
435    struct ContextLookupContext lookup_context = {
436      {match_class, apply_func},
437      &class_def
438    };
439    return rule_set.apply (c, lookup_context);
440  }
441
442  inline bool sanitize (hb_sanitize_context_t *c) {
443    TRACE_SANITIZE ();
444    return coverage.sanitize (c, this)
445        && classDef.sanitize (c, this)
446	&& ruleSet.sanitize (c, this);
447  }
448
449  private:
450  USHORT	format;			/* Format identifier--format = 2 */
451  OffsetTo<Coverage>
452		coverage;		/* Offset to Coverage table--from
453					 * beginning of table */
454  OffsetTo<ClassDef>
455		classDef;		/* Offset to glyph ClassDef table--from
456					 * beginning of table */
457  OffsetArrayOf<RuleSet>
458		ruleSet;		/* Array of RuleSet tables
459					 * ordered by class */
460  public:
461  DEFINE_SIZE_ARRAY (8, ruleSet);
462};
463
464
465struct ContextFormat3
466{
467  friend struct Context;
468
469  private:
470  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
471  {
472    TRACE_APPLY ();
473    unsigned int index = (this+coverage[0]) (c->buffer->info[c->buffer->idx].codepoint);
474    if (likely (index == NOT_COVERED))
475      return false;
476
477    const LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * glyphCount);
478    struct ContextLookupContext lookup_context = {
479      {match_coverage, apply_func},
480      this
481    };
482    return context_lookup (c,
483			   glyphCount, (const USHORT *) (coverage + 1),
484			   lookupCount, lookupRecord,
485			   lookup_context);
486  }
487
488  inline bool sanitize (hb_sanitize_context_t *c) {
489    TRACE_SANITIZE ();
490    if (!c->check_struct (this)) return false;
491    unsigned int count = glyphCount;
492    if (!c->check_array (coverage, coverage[0].static_size, count)) return false;
493    for (unsigned int i = 0; i < count; i++)
494      if (!coverage[i].sanitize (c, this)) return false;
495    LookupRecord *lookupRecord = &StructAtOffset<LookupRecord> (coverage, coverage[0].static_size * count);
496    return c->check_array (lookupRecord, lookupRecord[0].static_size, lookupCount);
497  }
498
499  private:
500  USHORT	format;			/* Format identifier--format = 3 */
501  USHORT	glyphCount;		/* Number of glyphs in the input glyph
502					 * sequence */
503  USHORT	lookupCount;		/* Number of LookupRecords */
504  OffsetTo<Coverage>
505		coverage[VAR];		/* Array of offsets to Coverage
506					 * table in glyph sequence order */
507  LookupRecord	lookupRecordX[VAR];	/* Array of LookupRecords--in
508					 * design order */
509  public:
510  DEFINE_SIZE_ARRAY2 (6, coverage, lookupRecordX);
511};
512
513struct Context
514{
515  protected:
516  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
517  {
518    TRACE_APPLY ();
519    switch (u.format) {
520    case 1: return u.format1.apply (c, apply_func);
521    case 2: return u.format2.apply (c, apply_func);
522    case 3: return u.format3.apply (c, apply_func);
523    default:return false;
524    }
525  }
526
527  inline bool sanitize (hb_sanitize_context_t *c) {
528    TRACE_SANITIZE ();
529    if (!u.format.sanitize (c)) return false;
530    switch (u.format) {
531    case 1: return u.format1.sanitize (c);
532    case 2: return u.format2.sanitize (c);
533    case 3: return u.format3.sanitize (c);
534    default:return true;
535    }
536  }
537
538  private:
539  union {
540  USHORT		format;		/* Format identifier */
541  ContextFormat1	format1;
542  ContextFormat2	format2;
543  ContextFormat3	format3;
544  } u;
545};
546
547
548/* Chaining Contextual lookups */
549
550struct ChainContextLookupContext
551{
552  ContextFuncs funcs;
553  const void *match_data[3];
554};
555
556static inline bool chain_context_lookup (hb_apply_context_t *c,
557					 unsigned int backtrackCount,
558					 const USHORT backtrack[],
559					 unsigned int inputCount, /* Including the first glyph (not matched) */
560					 const USHORT input[], /* Array of input values--start with second glyph */
561					 unsigned int lookaheadCount,
562					 const USHORT lookahead[],
563					 unsigned int lookupCount,
564					 const LookupRecord lookupRecord[],
565					 ChainContextLookupContext &lookup_context)
566{
567  /* First guess */
568  if (unlikely (c->buffer->backtrack_len () < backtrackCount ||
569		c->buffer->idx + inputCount + lookaheadCount > c->buffer->len ||
570		inputCount + lookaheadCount > c->context_length))
571    return false;
572
573  hb_apply_context_t new_context = *c;
574  return match_backtrack (c,
575			  backtrackCount, backtrack,
576			  lookup_context.funcs.match, lookup_context.match_data[0])
577      && match_input (c,
578		      inputCount, input,
579		      lookup_context.funcs.match, lookup_context.match_data[1],
580		      &new_context.context_length)
581      && match_lookahead (c,
582			  lookaheadCount, lookahead,
583			  lookup_context.funcs.match, lookup_context.match_data[2],
584			  new_context.context_length)
585      && apply_lookup (&new_context,
586		       inputCount,
587		       lookupCount, lookupRecord,
588		       lookup_context.funcs.apply);
589}
590
591struct ChainRule
592{
593  friend struct ChainRuleSet;
594
595  private:
596  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
597  {
598    TRACE_APPLY ();
599    const HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
600    const ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
601    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
602    return chain_context_lookup (c,
603				 backtrack.len, backtrack.array,
604				 input.len, input.array,
605				 lookahead.len, lookahead.array,
606				 lookup.len, lookup.array,
607				 lookup_context);
608  }
609
610  public:
611  inline bool sanitize (hb_sanitize_context_t *c) {
612    TRACE_SANITIZE ();
613    if (!backtrack.sanitize (c)) return false;
614    HeadlessArrayOf<USHORT> &input = StructAfter<HeadlessArrayOf<USHORT> > (backtrack);
615    if (!input.sanitize (c)) return false;
616    ArrayOf<USHORT> &lookahead = StructAfter<ArrayOf<USHORT> > (input);
617    if (!lookahead.sanitize (c)) return false;
618    ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
619    return lookup.sanitize (c);
620  }
621
622  private:
623  ArrayOf<USHORT>
624		backtrack;		/* Array of backtracking values
625					 * (to be matched before the input
626					 * sequence) */
627  HeadlessArrayOf<USHORT>
628		inputX;			/* Array of input values (start with
629					 * second glyph) */
630  ArrayOf<USHORT>
631		lookaheadX;		/* Array of lookahead values's (to be
632					 * matched after the input sequence) */
633  ArrayOf<LookupRecord>
634		lookupX;		/* Array of LookupRecords--in
635					 * design order) */
636  public:
637  DEFINE_SIZE_MIN (8);
638};
639
640struct ChainRuleSet
641{
642  inline bool apply (hb_apply_context_t *c, ChainContextLookupContext &lookup_context) const
643  {
644    TRACE_APPLY ();
645    unsigned int num_rules = rule.len;
646    for (unsigned int i = 0; i < num_rules; i++)
647    {
648      if ((this+rule[i]).apply (c, lookup_context))
649        return true;
650    }
651
652    return false;
653  }
654
655  inline bool sanitize (hb_sanitize_context_t *c) {
656    TRACE_SANITIZE ();
657    return rule.sanitize (c, this);
658  }
659
660  private:
661  OffsetArrayOf<ChainRule>
662		rule;			/* Array of ChainRule tables
663					 * ordered by preference */
664  public:
665  DEFINE_SIZE_ARRAY (2, rule);
666};
667
668struct ChainContextFormat1
669{
670  friend struct ChainContext;
671
672  private:
673  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
674  {
675    TRACE_APPLY ();
676    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
677    if (likely (index == NOT_COVERED))
678      return false;
679
680    const ChainRuleSet &rule_set = this+ruleSet[index];
681    struct ChainContextLookupContext lookup_context = {
682      {match_glyph, apply_func},
683      {NULL, NULL, NULL}
684    };
685    return rule_set.apply (c, lookup_context);
686  }
687
688  inline bool sanitize (hb_sanitize_context_t *c) {
689    TRACE_SANITIZE ();
690    return coverage.sanitize (c, this)
691	&& ruleSet.sanitize (c, this);
692  }
693
694  private:
695  USHORT	format;			/* Format identifier--format = 1 */
696  OffsetTo<Coverage>
697		coverage;		/* Offset to Coverage table--from
698					 * beginning of table */
699  OffsetArrayOf<ChainRuleSet>
700		ruleSet;		/* Array of ChainRuleSet tables
701					 * ordered by Coverage Index */
702  public:
703  DEFINE_SIZE_ARRAY (6, ruleSet);
704};
705
706struct ChainContextFormat2
707{
708  friend struct ChainContext;
709
710  private:
711  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
712  {
713    TRACE_APPLY ();
714    unsigned int index = (this+coverage) (c->buffer->info[c->buffer->idx].codepoint);
715    if (likely (index == NOT_COVERED))
716      return false;
717
718    const ClassDef &backtrack_class_def = this+backtrackClassDef;
719    const ClassDef &input_class_def = this+inputClassDef;
720    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
721
722    index = input_class_def (c->buffer->info[c->buffer->idx].codepoint);
723    const ChainRuleSet &rule_set = this+ruleSet[index];
724    struct ChainContextLookupContext lookup_context = {
725      {match_class, apply_func},
726      {&backtrack_class_def,
727       &input_class_def,
728       &lookahead_class_def}
729    };
730    return rule_set.apply (c, lookup_context);
731  }
732
733  inline bool sanitize (hb_sanitize_context_t *c) {
734    TRACE_SANITIZE ();
735    return coverage.sanitize (c, this)
736	&& backtrackClassDef.sanitize (c, this)
737	&& inputClassDef.sanitize (c, this)
738	&& lookaheadClassDef.sanitize (c, this)
739	&& ruleSet.sanitize (c, this);
740  }
741
742  private:
743  USHORT	format;			/* Format identifier--format = 2 */
744  OffsetTo<Coverage>
745		coverage;		/* Offset to Coverage table--from
746					 * beginning of table */
747  OffsetTo<ClassDef>
748		backtrackClassDef;	/* Offset to glyph ClassDef table
749					 * containing backtrack sequence
750					 * data--from beginning of table */
751  OffsetTo<ClassDef>
752		inputClassDef;		/* Offset to glyph ClassDef
753					 * table containing input sequence
754					 * data--from beginning of table */
755  OffsetTo<ClassDef>
756		lookaheadClassDef;	/* Offset to glyph ClassDef table
757					 * containing lookahead sequence
758					 * data--from beginning of table */
759  OffsetArrayOf<ChainRuleSet>
760		ruleSet;		/* Array of ChainRuleSet tables
761					 * ordered by class */
762  public:
763  DEFINE_SIZE_ARRAY (12, ruleSet);
764};
765
766struct ChainContextFormat3
767{
768  friend struct ChainContext;
769
770  private:
771
772  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
773  {
774    TRACE_APPLY ();
775    const OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
776
777    unsigned int index = (this+input[0]) (c->buffer->info[c->buffer->idx].codepoint);
778    if (likely (index == NOT_COVERED))
779      return false;
780
781    const OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
782    const ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
783    struct ChainContextLookupContext lookup_context = {
784      {match_coverage, apply_func},
785      {this, this, this}
786    };
787    return chain_context_lookup (c,
788				 backtrack.len, (const USHORT *) backtrack.array,
789				 input.len, (const USHORT *) input.array + 1,
790				 lookahead.len, (const USHORT *) lookahead.array,
791				 lookup.len, lookup.array,
792				 lookup_context);
793  }
794
795  inline bool sanitize (hb_sanitize_context_t *c) {
796    TRACE_SANITIZE ();
797    if (!backtrack.sanitize (c, this)) return false;
798    OffsetArrayOf<Coverage> &input = StructAfter<OffsetArrayOf<Coverage> > (backtrack);
799    if (!input.sanitize (c, this)) return false;
800    OffsetArrayOf<Coverage> &lookahead = StructAfter<OffsetArrayOf<Coverage> > (input);
801    if (!lookahead.sanitize (c, this)) return false;
802    ArrayOf<LookupRecord> &lookup = StructAfter<ArrayOf<LookupRecord> > (lookahead);
803    return lookup.sanitize (c);
804  }
805
806  private:
807  USHORT	format;			/* Format identifier--format = 3 */
808  OffsetArrayOf<Coverage>
809		backtrack;		/* Array of coverage tables
810					 * in backtracking sequence, in  glyph
811					 * sequence order */
812  OffsetArrayOf<Coverage>
813		inputX		;	/* Array of coverage
814					 * tables in input sequence, in glyph
815					 * sequence order */
816  OffsetArrayOf<Coverage>
817		lookaheadX;		/* Array of coverage tables
818					 * in lookahead sequence, in glyph
819					 * sequence order */
820  ArrayOf<LookupRecord>
821		lookupX;		/* Array of LookupRecords--in
822					 * design order) */
823  public:
824  DEFINE_SIZE_MIN (10);
825};
826
827struct ChainContext
828{
829  protected:
830  inline bool apply (hb_apply_context_t *c, apply_lookup_func_t apply_func) const
831  {
832    TRACE_APPLY ();
833    switch (u.format) {
834    case 1: return u.format1.apply (c, apply_func);
835    case 2: return u.format2.apply (c, apply_func);
836    case 3: return u.format3.apply (c, apply_func);
837    default:return false;
838    }
839  }
840
841  inline bool sanitize (hb_sanitize_context_t *c) {
842    TRACE_SANITIZE ();
843    if (!u.format.sanitize (c)) return false;
844    switch (u.format) {
845    case 1: return u.format1.sanitize (c);
846    case 2: return u.format2.sanitize (c);
847    case 3: return u.format3.sanitize (c);
848    default:return true;
849    }
850  }
851
852  private:
853  union {
854  USHORT		format;	/* Format identifier */
855  ChainContextFormat1	format1;
856  ChainContextFormat2	format2;
857  ChainContextFormat3	format3;
858  } u;
859};
860
861
862struct ExtensionFormat1
863{
864  friend struct Extension;
865
866  protected:
867  inline unsigned int get_type (void) const { return extensionLookupType; }
868  inline unsigned int get_offset (void) const { return extensionOffset; }
869
870  inline bool sanitize (hb_sanitize_context_t *c) {
871    TRACE_SANITIZE ();
872    return c->check_struct (this);
873  }
874
875  private:
876  USHORT	format;			/* Format identifier. Set to 1. */
877  USHORT	extensionLookupType;	/* Lookup type of subtable referenced
878					 * by ExtensionOffset (i.e. the
879					 * extension subtable). */
880  ULONG		extensionOffset;	/* Offset to the extension subtable,
881					 * of lookup type subtable. */
882  public:
883  DEFINE_SIZE_STATIC (8);
884};
885
886struct Extension
887{
888  inline unsigned int get_type (void) const
889  {
890    switch (u.format) {
891    case 1: return u.format1.get_type ();
892    default:return 0;
893    }
894  }
895  inline unsigned int get_offset (void) const
896  {
897    switch (u.format) {
898    case 1: return u.format1.get_offset ();
899    default:return 0;
900    }
901  }
902
903  inline bool sanitize (hb_sanitize_context_t *c) {
904    TRACE_SANITIZE ();
905    if (!u.format.sanitize (c)) return false;
906    switch (u.format) {
907    case 1: return u.format1.sanitize (c);
908    default:return true;
909    }
910  }
911
912  private:
913  union {
914  USHORT		format;		/* Format identifier */
915  ExtensionFormat1	format1;
916  } u;
917};
918
919
920/*
921 * GSUB/GPOS Common
922 */
923
924struct GSUBGPOS
925{
926  static const hb_tag_t GSUBTag	= HB_OT_TAG_GSUB;
927  static const hb_tag_t GPOSTag	= HB_OT_TAG_GPOS;
928
929  inline unsigned int get_script_count (void) const
930  { return (this+scriptList).len; }
931  inline const Tag& get_script_tag (unsigned int i) const
932  { return (this+scriptList).get_tag (i); }
933  inline unsigned int get_script_tags (unsigned int start_offset,
934				       unsigned int *script_count /* IN/OUT */,
935				       hb_tag_t     *script_tags /* OUT */) const
936  { return (this+scriptList).get_tags (start_offset, script_count, script_tags); }
937  inline const Script& get_script (unsigned int i) const
938  { return (this+scriptList)[i]; }
939  inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
940  { return (this+scriptList).find_index (tag, index); }
941
942  inline unsigned int get_feature_count (void) const
943  { return (this+featureList).len; }
944  inline const Tag& get_feature_tag (unsigned int i) const
945  { return (this+featureList).get_tag (i); }
946  inline unsigned int get_feature_tags (unsigned int start_offset,
947					unsigned int *feature_count /* IN/OUT */,
948					hb_tag_t     *feature_tags /* OUT */) const
949  { return (this+featureList).get_tags (start_offset, feature_count, feature_tags); }
950  inline const Feature& get_feature (unsigned int i) const
951  { return (this+featureList)[i]; }
952  inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const
953  { return (this+featureList).find_index (tag, index); }
954
955  inline unsigned int get_lookup_count (void) const
956  { return (this+lookupList).len; }
957  inline const Lookup& get_lookup (unsigned int i) const
958  { return (this+lookupList)[i]; }
959
960  inline bool sanitize (hb_sanitize_context_t *c) {
961    TRACE_SANITIZE ();
962    return version.sanitize (c) && likely (version.major == 1)
963	&& scriptList.sanitize (c, this)
964	&& featureList.sanitize (c, this)
965	&& lookupList.sanitize (c, this);
966  }
967
968  protected:
969  FixedVersion	version;	/* Version of the GSUB/GPOS table--initially set
970				 * to 0x00010000 */
971  OffsetTo<ScriptList>
972		scriptList;  	/* ScriptList table */
973  OffsetTo<FeatureList>
974		featureList; 	/* FeatureList table */
975  OffsetTo<LookupList>
976		lookupList; 	/* LookupList table */
977  public:
978  DEFINE_SIZE_STATIC (10);
979};
980
981
982
983#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
984