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