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