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