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