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