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