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