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