[Hb] Use reinterpret casts instead of direct casts to char *
[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 = *(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 LookupRecord *)
260                                        (CONST_CHARP(input) +
261                                         sizeof (input[0]) * (inputCount ? inputCount - 1 : 0));
262     return context_lookup (APPLY_ARG,
263                            inputCount, input,
264                            lookupCount, lookupRecord,
265                            lookup_context);
266   }
267
268   public:
269   inline bool sanitize (SANITIZE_ARG_DEF) {
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     return SANITIZE_THIS (rule);
304   }
305
306   private:
307   OffsetArrayOf<Rule>
308                 rule;                   /* Array of Rule tables
309                                          * ordered by preference */
310 };
311
312
313 struct ContextFormat1
314 {
315   friend struct Context;
316
317   private:
318   inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
319   {
320     unsigned int index = (this+coverage) (IN_CURGLYPH ());
321     if (HB_LIKELY (index == NOT_COVERED))
322       return false;
323
324     const RuleSet &rule_set = this+ruleSet[index];
325     struct ContextLookupContext lookup_context = {
326       {match_glyph, apply_func},
327       NULL
328     };
329     return rule_set.apply (APPLY_ARG, lookup_context);
330   }
331
332   inline bool sanitize (SANITIZE_ARG_DEF) {
333     return SANITIZE_THIS2 (coverage, ruleSet);
334   }
335
336   private:
337   USHORT        format;                 /* Format identifier--format = 1 */
338   OffsetTo<Coverage>
339                 coverage;               /* Offset to Coverage table--from
340                                          * beginning of table */
341   OffsetArrayOf<RuleSet>
342                 ruleSet;                /* Array of RuleSet tables
343                                          * ordered by Coverage Index */
344 };
345 ASSERT_SIZE (ContextFormat1, 6);
346
347
348 struct ContextFormat2
349 {
350   friend struct Context;
351
352   private:
353   inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
354   {
355     unsigned int index = (this+coverage) (IN_CURGLYPH ());
356     if (HB_LIKELY (index == NOT_COVERED))
357       return false;
358
359     const ClassDef &class_def = this+classDef;
360     index = class_def (IN_CURGLYPH ());
361     const RuleSet &rule_set = this+ruleSet[index];
362     /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
363      * them across subrule lookups.  Not sure it's worth it.
364      */
365     struct ContextLookupContext lookup_context = {
366      {match_class, apply_func},
367       CHARP(&class_def)
368     };
369     return rule_set.apply (APPLY_ARG, lookup_context);
370   }
371
372   inline bool sanitize (SANITIZE_ARG_DEF) {
373     return SANITIZE_THIS3 (coverage, classDef, ruleSet);
374   }
375
376   private:
377   USHORT        format;                 /* Format identifier--format = 2 */
378   OffsetTo<Coverage>
379                 coverage;               /* Offset to Coverage table--from
380                                          * beginning of table */
381   OffsetTo<ClassDef>
382                 classDef;               /* Offset to glyph ClassDef table--from
383                                          * beginning of table */
384   OffsetArrayOf<RuleSet>
385                 ruleSet;                /* Array of RuleSet tables
386                                          * ordered by class */
387 };
388 ASSERT_SIZE (ContextFormat2, 8);
389
390
391 struct ContextFormat3
392 {
393   friend struct Context;
394
395   private:
396   inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
397   {
398     unsigned int index = (this+coverage[0]) (IN_CURGLYPH ());
399     if (HB_LIKELY (index == NOT_COVERED))
400       return false;
401
402     const LookupRecord *lookupRecord = (const LookupRecord *)
403                                        (CONST_CHARP(coverage) +
404                                         sizeof (coverage[0]) * glyphCount);
405     struct ContextLookupContext lookup_context = {
406       {match_coverage, apply_func},
407       CHARP(this)
408     };
409     return context_lookup (APPLY_ARG,
410                            glyphCount, (const USHORT *) (coverage + 1),
411                            lookupCount, lookupRecord,
412                            lookup_context);
413   }
414
415   inline bool sanitize (SANITIZE_ARG_DEF) {
416     if (!SANITIZE_SELF ()) return false;
417     unsigned int count = glyphCount;
418     for (unsigned int i = 0; i < count; i++)
419       if (!SANITIZE_THIS (coverage[i])) return false;
420     LookupRecord *lookupRecord = (LookupRecord *)
421                                  (CHARP(coverage) +
422                                   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   inline bool sanitize (SANITIZE_ARG_DEF) {
453     if (!SANITIZE (u.format)) return false;
454     switch (u.format) {
455     case 1: return u.format1->sanitize (SANITIZE_ARG);
456     case 2: return u.format2->sanitize (SANITIZE_ARG);
457     case 3: return u.format3->sanitize (SANITIZE_ARG);
458     default:return true;
459     }
460   }
461
462   private:
463   union {
464   USHORT                format;         /* Format identifier */
465   ContextFormat1        format1[];
466   ContextFormat2        format2[];
467   ContextFormat3        format3[];
468   } u;
469 };
470 ASSERT_SIZE (Context, 2);
471
472
473 /* Chaining Contextual lookups */
474
475 struct ChainContextLookupContext
476 {
477   ContextFuncs funcs;
478   char *match_data[3];
479 };
480
481 static inline bool chain_context_lookup (APPLY_ARG_DEF,
482                                          unsigned int backtrackCount,
483                                          const USHORT backtrack[],
484                                          unsigned int inputCount, /* Including the first glyph (not matched) */
485                                          const USHORT input[], /* Array of input values--start with second glyph */
486                                          unsigned int lookaheadCount,
487                                          const USHORT lookahead[],
488                                          unsigned int lookupCount,
489                                          const LookupRecord lookupRecord[],
490                                          ChainContextLookupContext &lookup_context)
491 {
492   /* First guess */
493   if (HB_UNLIKELY (buffer->out_pos < backtrackCount ||
494                    buffer->in_pos + inputCount + lookaheadCount > buffer->in_length ||
495                    inputCount + lookaheadCount > context_length))
496     return false;
497
498   unsigned int offset;
499   return match_backtrack (APPLY_ARG,
500                           backtrackCount, backtrack,
501                           lookup_context.funcs.match, lookup_context.match_data[0]) &&
502          match_input (APPLY_ARG,
503                       inputCount, input,
504                       lookup_context.funcs.match, lookup_context.match_data[1],
505                       &offset) &&
506          match_lookahead (APPLY_ARG,
507                           lookaheadCount, lookahead,
508                           lookup_context.funcs.match, lookup_context.match_data[2],
509                           offset) &&
510          (context_length = offset, true) &&
511          apply_lookup (APPLY_ARG,
512                        inputCount,
513                        lookupCount, lookupRecord,
514                        lookup_context.funcs.apply);
515 }
516
517 struct ChainRule
518 {
519   friend struct ChainRuleSet;
520
521   private:
522   inline bool apply (APPLY_ARG_DEF, ChainContextLookupContext &lookup_context) const
523   {
524     const HeadlessArrayOf<USHORT> &input = *(const HeadlessArrayOf<USHORT>*)
525                                             (CONST_CHARP(&backtrack) + backtrack.get_size ());
526     const ArrayOf<USHORT> &lookahead = *(const ArrayOf<USHORT>*)
527                                         (CONST_CHARP(&input) + input.get_size ());
528     const ArrayOf<LookupRecord> &lookup = *(const ArrayOf<LookupRecord>*)
529                                            (CONST_CHARP(&lookahead) + lookahead.get_size ());
530     return chain_context_lookup (APPLY_ARG,
531                                  backtrack.len, backtrack.array,
532                                  input.len, input.array + 1,
533                                  lookahead.len, lookahead.array,
534                                  lookup.len, lookup.array,
535                                  lookup_context);
536     return false;
537   }
538
539   public:
540   inline bool sanitize (SANITIZE_ARG_DEF) {
541     if (!SANITIZE (backtrack)) return false;
542     HeadlessArrayOf<USHORT> &input = *(HeadlessArrayOf<USHORT>*)
543                                       (CHARP(&backtrack) + backtrack.get_size ());
544     if (!SANITIZE (input)) return false;
545     ArrayOf<USHORT> &lookahead = *(ArrayOf<USHORT>*)
546                                   (CHARP(&input) + input.get_size ());
547     if (!SANITIZE (lookahead)) return false;
548     ArrayOf<LookupRecord> &lookup = *(ArrayOf<LookupRecord>*)
549                                      (CHARP(&lookahead) + lookahead.get_size ());
550     return SANITIZE (lookup);
551   }
552
553   private:
554   ArrayOf<USHORT>
555                 backtrack;              /* Array of backtracking values
556                                          * (to be matched before the input
557                                          * sequence) */
558   HeadlessArrayOf<USHORT>
559                 inputX;                 /* Array of input values (start with
560                                          * second glyph) */
561   ArrayOf<USHORT>
562                 lookaheadX;             /* Array of lookahead values's (to be
563                                          * matched after the input sequence) */
564   ArrayOf<LookupRecord>
565                 lookupX;                /* Array of LookupRecords--in
566                                          * design order) */
567 };
568 ASSERT_SIZE (ChainRule, 8);
569
570 struct ChainRuleSet
571 {
572   inline bool apply (APPLY_ARG_DEF, ChainContextLookupContext &lookup_context) const
573   {
574     unsigned int num_rules = rule.len;
575     for (unsigned int i = 0; i < num_rules; i++)
576     {
577       if ((this+rule[i]).apply (APPLY_ARG, lookup_context))
578         return true;
579     }
580
581     return false;
582   }
583
584   inline bool sanitize (SANITIZE_ARG_DEF) {
585     return SANITIZE_THIS (rule);
586   }
587
588   private:
589   OffsetArrayOf<ChainRule>
590                 rule;                   /* Array of ChainRule tables
591                                          * ordered by preference */
592 };
593 ASSERT_SIZE (ChainRuleSet, 2);
594
595 struct ChainContextFormat1
596 {
597   friend struct ChainContext;
598
599   private:
600   inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
601   {
602     unsigned int index = (this+coverage) (IN_CURGLYPH ());
603     if (HB_LIKELY (index == NOT_COVERED))
604       return false;
605
606     const ChainRuleSet &rule_set = this+ruleSet[index];
607     struct ChainContextLookupContext lookup_context = {
608       {match_glyph, apply_func},
609       {NULL, NULL, NULL}
610     };
611     return rule_set.apply (APPLY_ARG, lookup_context);
612   }
613
614   inline bool sanitize (SANITIZE_ARG_DEF) {
615     return SANITIZE_THIS2 (coverage, ruleSet);
616   }
617
618   private:
619   USHORT        format;                 /* Format identifier--format = 1 */
620   OffsetTo<Coverage>
621                 coverage;               /* Offset to Coverage table--from
622                                          * beginning of table */
623   OffsetArrayOf<ChainRuleSet>
624                 ruleSet;                /* Array of ChainRuleSet tables
625                                          * ordered by Coverage Index */
626 };
627 ASSERT_SIZE (ChainContextFormat1, 6);
628
629 struct ChainContextFormat2
630 {
631   friend struct ChainContext;
632
633   private:
634   inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
635   {
636     unsigned int index = (this+coverage) (IN_CURGLYPH ());
637     if (HB_LIKELY (index == NOT_COVERED))
638       return false;
639
640     const ClassDef &backtrack_class_def = this+backtrackClassDef;
641     const ClassDef &input_class_def = this+inputClassDef;
642     const ClassDef &lookahead_class_def = this+lookaheadClassDef;
643
644     index = input_class_def (IN_CURGLYPH ());
645     const ChainRuleSet &rule_set = this+ruleSet[index];
646     /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
647      * them across subrule lookups.  Not sure it's worth it.
648      */
649     struct ChainContextLookupContext lookup_context = {
650      {match_class, apply_func},
651      {CHARP(&backtrack_class_def),
652       CHARP(&input_class_def),
653       CHARP(&lookahead_class_def)}
654     };
655     return rule_set.apply (APPLY_ARG, lookup_context);
656   }
657
658   inline bool sanitize (SANITIZE_ARG_DEF) {
659     return SANITIZE_THIS2 (coverage, backtrackClassDef) &&
660            SANITIZE_THIS2 (inputClassDef, lookaheadClassDef) &&
661            SANITIZE_THIS (ruleSet);
662   }
663
664   private:
665   USHORT        format;                 /* Format identifier--format = 2 */
666   OffsetTo<Coverage>
667                 coverage;               /* Offset to Coverage table--from
668                                          * beginning of table */
669   OffsetTo<ClassDef>
670                 backtrackClassDef;      /* Offset to glyph ClassDef table
671                                          * containing backtrack sequence
672                                          * data--from beginning of table */
673   OffsetTo<ClassDef>
674                 inputClassDef;          /* Offset to glyph ClassDef
675                                          * table containing input sequence
676                                          * data--from beginning of table */
677   OffsetTo<ClassDef>
678                 lookaheadClassDef;      /* Offset to glyph ClassDef table
679                                          * containing lookahead sequence
680                                          * data--from beginning of table */
681   OffsetArrayOf<ChainRuleSet>
682                 ruleSet;                /* Array of ChainRuleSet tables
683                                          * ordered by class */
684 };
685 ASSERT_SIZE (ChainContextFormat2, 12);
686
687 struct ChainContextFormat3
688 {
689   friend struct ChainContext;
690
691   private:
692
693   inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
694   {
695     const OffsetArrayOf<Coverage> &input = *(const OffsetArrayOf<Coverage>*)
696                                             (CONST_CHARP(&backtrack) + backtrack.get_size ());
697
698     unsigned int index = (this+input[0]) (IN_CURGLYPH ());
699     if (HB_LIKELY (index == NOT_COVERED))
700       return false;
701
702     const OffsetArrayOf<Coverage> &lookahead = *(const OffsetArrayOf<Coverage>*)
703                                                 (CONST_CHARP(&input) + input.get_size ());
704     const ArrayOf<LookupRecord> &lookup = *(const ArrayOf<LookupRecord>*)
705                                            (CONST_CHARP(&lookahead) + lookahead.get_size ());
706     struct ChainContextLookupContext lookup_context = {
707       {match_coverage, apply_func},
708       {CHARP(this), CHARP(this), CHARP(this)}
709     };
710     return chain_context_lookup (APPLY_ARG,
711                                  backtrack.len, (USHORT *) backtrack.array,
712                                  input.len, (USHORT *) input.array,
713                                  lookahead.len, (USHORT *) lookahead.array,
714                                  lookup.len, lookup.array,
715                                  lookup_context);
716     return false;
717   }
718
719   inline bool sanitize (SANITIZE_ARG_DEF) {
720     if (!SANITIZE_THIS (backtrack)) return false;
721     OffsetArrayOf<Coverage> &input = *(OffsetArrayOf<Coverage>*)
722                                       (CHARP(&backtrack) + backtrack.get_size ());
723     if (!SANITIZE_THIS (input)) return false;
724     OffsetArrayOf<Coverage> &lookahead = *(OffsetArrayOf<Coverage>*)
725                                           (CHARP(&input) + input.get_size ());
726     if (!SANITIZE_THIS (lookahead)) return false;
727     ArrayOf<LookupRecord> &lookup = *(ArrayOf<LookupRecord>*)
728                                      (CHARP(&lookahead) + lookahead.get_size ());
729     return SANITIZE (lookup);
730   }
731
732   private:
733   USHORT        format;                 /* Format identifier--format = 3 */
734   OffsetArrayOf<Coverage>
735                 backtrack;              /* Array of coverage tables
736                                          * in backtracking sequence, in  glyph
737                                          * sequence order */
738   OffsetArrayOf<Coverage>
739                 inputX          ;       /* Array of coverage
740                                          * tables in input sequence, in glyph
741                                          * sequence order */
742   OffsetArrayOf<Coverage>
743                 lookaheadX;             /* Array of coverage tables
744                                          * in lookahead sequence, in glyph
745                                          * sequence order */
746   ArrayOf<LookupRecord>
747                 lookupX;                /* Array of LookupRecords--in
748                                          * design order) */
749 };
750 ASSERT_SIZE (ChainContextFormat3, 10);
751
752 struct ChainContext
753 {
754   protected:
755   bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
756   {
757     switch (u.format) {
758     case 1: return u.format1->apply (APPLY_ARG, apply_func);
759     case 2: return u.format2->apply (APPLY_ARG, apply_func);
760     case 3: return u.format3->apply (APPLY_ARG, apply_func);
761     default:return false;
762     }
763   }
764
765   inline bool sanitize (SANITIZE_ARG_DEF) {
766     if (!SANITIZE (u.format)) return false;
767     switch (u.format) {
768     case 1: return u.format1->sanitize (SANITIZE_ARG);
769     case 2: return u.format2->sanitize (SANITIZE_ARG);
770     case 3: return u.format3->sanitize (SANITIZE_ARG);
771     default:return true;
772     }
773   }
774
775   private:
776   union {
777   USHORT                format; /* Format identifier */
778   ChainContextFormat1   format1[];
779   ChainContextFormat2   format2[];
780   ChainContextFormat3   format3[];
781   } u;
782 };
783 ASSERT_SIZE (ChainContext, 2);
784
785
786 struct ExtensionFormat1
787 {
788   friend struct Extension;
789
790   private:
791   inline unsigned int get_type (void) const { return extensionLookupType; }
792   inline unsigned int get_offset (void) const { return (extensionOffset[0] << 16) + extensionOffset[1]; }
793   inline const LookupSubTable& get_subtable (void) const
794   {
795     unsigned int offset = get_offset ();
796     if (HB_UNLIKELY (!offset)) return Null(LookupSubTable);
797     return *(LookupSubTable*)(CHARP(this) + offset);
798   }
799
800   inline bool sanitize (SANITIZE_ARG_DEF) {
801     return SANITIZE_SELF ();
802   }
803
804   private:
805   USHORT        format;                 /* Format identifier. Set to 1. */
806   USHORT        extensionLookupType;    /* Lookup type of subtable referenced
807                                          * by ExtensionOffset (i.e. the
808                                          * extension subtable). */
809   USHORT        extensionOffset[2];     /* Offset to the extension subtable,
810                                          * of lookup type subtable.
811                                          * Defined as two shorts to avoid
812                                          * alignment requirements. */
813 };
814 ASSERT_SIZE (ExtensionFormat1, 8);
815
816 struct Extension
817 {
818   inline unsigned int get_type (void) const
819   {
820     switch (u.format) {
821     case 1: return u.format1->get_type ();
822     default:return 0;
823     }
824   }
825   inline const LookupSubTable& get_subtable (void) const
826   {
827     switch (u.format) {
828     case 1: return u.format1->get_subtable ();
829     default:return Null(LookupSubTable);
830     }
831   }
832
833   inline bool sanitize (SANITIZE_ARG_DEF) {
834     if (!SANITIZE (u.format)) return false;
835     switch (u.format) {
836     case 1: return u.format1->sanitize (SANITIZE_ARG);
837     default:return true;
838     }
839   }
840
841   private:
842   union {
843   USHORT                format;         /* Format identifier */
844   ExtensionFormat1      format1[];
845   } u;
846 };
847 ASSERT_SIZE (Extension, 2);
848
849
850 /*
851  * GSUB/GPOS Common
852  */
853
854 struct GSUBGPOS
855 {
856   static const hb_tag_t GSUBTag         = HB_TAG ('G','S','U','B');
857   static const hb_tag_t GPOSTag         = HB_TAG ('G','P','O','S');
858
859   STATIC_DEFINE_GET_FOR_DATA_CHECK_MAJOR_VERSION (GSUBGPOS, 1, 1);
860
861   DEFINE_TAG_LIST_INTERFACE (Script,  script ); /* get_script_count (), get_script (i), get_script_tag (i) */
862   DEFINE_TAG_LIST_INTERFACE (Feature, feature); /* get_feature_count(), get_feature(i), get_feature_tag(i) */
863   DEFINE_LIST_INTERFACE     (Lookup,  lookup ); /* get_lookup_count (), get_lookup (i) */
864
865   // LONGTERMTODO bsearch
866   DEFINE_TAG_FIND_INTERFACE (Script,  script ); /* find_script_index (), get_script_by_tag (tag) */
867   DEFINE_TAG_FIND_INTERFACE (Feature, feature); /* find_feature_index(), get_feature_by_tag(tag) */
868
869   inline bool sanitize (SANITIZE_ARG_DEF) {
870     if (!SANITIZE (version)) return false;
871     if (version.major != 1) return true;
872     return SANITIZE_THIS3 (scriptList, featureList, lookupList);
873   }
874
875   protected:
876   FixedVersion  version;        /* Version of the GSUB/GPOS table--initially set
877                                  * to 0x00010000 */
878   OffsetTo<ScriptList>
879                 scriptList;     /* ScriptList table */
880   OffsetTo<FeatureList>
881                 featureList;    /* FeatureList table */
882   OffsetTo<LookupList>
883                 lookupList;     /* LookupList table */
884 };
885 ASSERT_SIZE (GSUBGPOS, 10);
886
887
888 #endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */