hb-ot-layout-gsubgpos-private.hh revision c0ab43c05833e8fc06d770a89370bec58a627e74
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
83typedef bool (*match_func_t) (hb_codepoint_t glyph_id, const USHORT &value, char *data);
84typedef bool (*apply_lookup_func_t) (APPLY_ARG_DEF, unsigned int lookup_index);
85
86struct ContextFuncs
87{
88  match_func_t match;
89  apply_lookup_func_t apply;
90};
91
92
93static inline bool match_glyph (hb_codepoint_t glyph_id, const USHORT &value, char *data)
94{
95  return glyph_id == value;
96}
97
98static 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
104static 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
111static 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
141static 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
166static 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
195struct 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};
208ASSERT_SIZE (LookupRecord, 4);
209
210static 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; i++)
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      i += buffer->in_pos - old_pos;
242      if (HB_UNLIKELY (buffer->in_pos == end))
243	return true;
244
245      if (!done)
246	goto not_applied;
247    }
248    else
249    {
250    not_applied:
251      /* No lookup applied for this index */
252      _hb_buffer_next_glyph (buffer);
253      i++;
254    }
255  }
256
257  return true;
258}
259
260
261/* Contextual lookups */
262
263struct ContextLookupContext
264{
265  ContextFuncs funcs;
266  char *match_data;
267};
268
269static inline bool context_lookup (APPLY_ARG_DEF,
270				   unsigned int inputCount, /* Including the first glyph (not matched) */
271				   const USHORT input[], /* Array of input values--start with second glyph */
272				   unsigned int lookupCount,
273				   const LookupRecord lookupRecord[],
274				   ContextLookupContext &lookup_context)
275{
276  return match_input (APPLY_ARG,
277		      inputCount, input,
278		      lookup_context.funcs.match, lookup_context.match_data,
279		      &context_length) &&
280	 apply_lookup (APPLY_ARG,
281		       inputCount,
282		       lookupCount, lookupRecord,
283		       lookup_context.funcs.apply);
284}
285
286struct Rule
287{
288  friend struct RuleSet;
289
290  private:
291  inline bool apply (APPLY_ARG_DEF, ContextLookupContext &lookup_context) const
292  {
293    TRACE_APPLY ();
294    const LookupRecord *lookupRecord = &CONST_CAST (LookupRecord, input, sizeof (input[0]) * (inputCount ? inputCount - 1 : 0));
295    return context_lookup (APPLY_ARG,
296			   inputCount, input,
297			   lookupCount, lookupRecord,
298			   lookup_context);
299  }
300
301  public:
302  inline bool sanitize (SANITIZE_ARG_DEF) {
303    TRACE_SANITIZE ();
304    if (!SANITIZE_SELF ()) return false;
305    return SANITIZE_MEM (input,
306			 sizeof (input[0]) * inputCount +
307			 sizeof (lookupRecordX[0]) * lookupCount);
308  }
309
310  private:
311  USHORT	inputCount;		/* Total number of glyphs in input
312					 * glyph sequence--includes the  first
313					 * glyph */
314  USHORT	lookupCount;		/* Number of LookupRecords */
315  USHORT	input[];		/* Array of match inputs--start with
316					 * second glyph */
317  LookupRecord	lookupRecordX[];	/* Array of LookupRecords--in
318					 * design order */
319};
320ASSERT_SIZE (Rule, 4);
321
322struct RuleSet
323{
324  inline bool apply (APPLY_ARG_DEF, ContextLookupContext &lookup_context) const
325  {
326    TRACE_APPLY ();
327    unsigned int num_rules = rule.len;
328    for (unsigned int i = 0; i < num_rules; i++)
329    {
330      if ((this+rule[i]).apply (APPLY_ARG, lookup_context))
331        return true;
332    }
333
334    return false;
335  }
336
337  inline bool sanitize (SANITIZE_ARG_DEF) {
338    TRACE_SANITIZE ();
339    return SANITIZE_THIS (rule);
340  }
341
342  private:
343  OffsetArrayOf<Rule>
344		rule;			/* Array of Rule tables
345					 * ordered by preference */
346};
347
348
349struct ContextFormat1
350{
351  friend struct Context;
352
353  private:
354  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
355  {
356    TRACE_APPLY ();
357    unsigned int index = (this+coverage) (IN_CURGLYPH ());
358    if (HB_LIKELY (index == NOT_COVERED))
359      return false;
360
361    const RuleSet &rule_set = this+ruleSet[index];
362    struct ContextLookupContext lookup_context = {
363      {match_glyph, apply_func},
364      NULL
365    };
366    return rule_set.apply (APPLY_ARG, lookup_context);
367  }
368
369  inline bool sanitize (SANITIZE_ARG_DEF) {
370    TRACE_SANITIZE ();
371    return SANITIZE_THIS2 (coverage, ruleSet);
372  }
373
374  private:
375  USHORT	format;			/* Format identifier--format = 1 */
376  OffsetTo<Coverage>
377		coverage;		/* Offset to Coverage table--from
378					 * beginning of table */
379  OffsetArrayOf<RuleSet>
380		ruleSet;		/* Array of RuleSet tables
381					 * ordered by Coverage Index */
382};
383ASSERT_SIZE (ContextFormat1, 6);
384
385
386struct ContextFormat2
387{
388  friend struct Context;
389
390  private:
391  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
392  {
393    TRACE_APPLY ();
394    unsigned int index = (this+coverage) (IN_CURGLYPH ());
395    if (HB_LIKELY (index == NOT_COVERED))
396      return false;
397
398    const ClassDef &class_def = this+classDef;
399    index = class_def (IN_CURGLYPH ());
400    const RuleSet &rule_set = this+ruleSet[index];
401    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
402     * them across subrule lookups.  Not sure it's worth it.
403     */
404    struct ContextLookupContext lookup_context = {
405     {match_class, apply_func},
406      DECONST_CHARP(&class_def)
407    };
408    return rule_set.apply (APPLY_ARG, lookup_context);
409  }
410
411  inline bool sanitize (SANITIZE_ARG_DEF) {
412    TRACE_SANITIZE ();
413    return SANITIZE_THIS3 (coverage, classDef, ruleSet);
414  }
415
416  private:
417  USHORT	format;			/* Format identifier--format = 2 */
418  OffsetTo<Coverage>
419		coverage;		/* Offset to Coverage table--from
420					 * beginning of table */
421  OffsetTo<ClassDef>
422		classDef;		/* Offset to glyph ClassDef table--from
423					 * beginning of table */
424  OffsetArrayOf<RuleSet>
425		ruleSet;		/* Array of RuleSet tables
426					 * ordered by class */
427};
428ASSERT_SIZE (ContextFormat2, 8);
429
430
431struct ContextFormat3
432{
433  friend struct Context;
434
435  private:
436  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
437  {
438    TRACE_APPLY ();
439    unsigned int index = (this+coverage[0]) (IN_CURGLYPH ());
440    if (HB_LIKELY (index == NOT_COVERED))
441      return false;
442
443    const LookupRecord *lookupRecord = &CONST_CAST(LookupRecord, coverage, sizeof (coverage[0]) * glyphCount);
444    struct ContextLookupContext lookup_context = {
445      {match_coverage, apply_func},
446      DECONST_CHARP(this)
447    };
448    return context_lookup (APPLY_ARG,
449			   glyphCount, (const USHORT *) (coverage + 1),
450			   lookupCount, lookupRecord,
451			   lookup_context);
452  }
453
454  inline bool sanitize (SANITIZE_ARG_DEF) {
455    TRACE_SANITIZE ();
456    if (!SANITIZE_SELF ()) return false;
457    unsigned int count = glyphCount;
458    for (unsigned int i = 0; i < count; i++)
459      if (!SANITIZE_THIS (coverage[i])) return false;
460    LookupRecord *lookupRecord = &CAST(LookupRecord, coverage, sizeof (coverage[0]) * glyphCount);
461    return SANITIZE_MEM (lookupRecord, sizeof (lookupRecord[0]) * lookupCount);
462  }
463
464  private:
465  USHORT	format;			/* Format identifier--format = 3 */
466  USHORT	glyphCount;		/* Number of glyphs in the input glyph
467					 * sequence */
468  USHORT	lookupCount;		/* Number of LookupRecords */
469  OffsetTo<Coverage>
470		coverage[];		/* Array of offsets to Coverage
471					 * table in glyph sequence order */
472  LookupRecord	lookupRecordX[];	/* Array of LookupRecords--in
473					 * design order */
474};
475ASSERT_SIZE (ContextFormat3, 6);
476
477struct Context
478{
479  protected:
480  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
481  {
482    TRACE_APPLY ();
483    switch (u.format) {
484    case 1: return u.format1->apply (APPLY_ARG, apply_func);
485    case 2: return u.format2->apply (APPLY_ARG, apply_func);
486    case 3: return u.format3->apply (APPLY_ARG, apply_func);
487    default:return false;
488    }
489  }
490
491  inline bool sanitize (SANITIZE_ARG_DEF) {
492    TRACE_SANITIZE ();
493    if (!SANITIZE (u.format)) return false;
494    switch (u.format) {
495    case 1: return u.format1->sanitize (SANITIZE_ARG);
496    case 2: return u.format2->sanitize (SANITIZE_ARG);
497    case 3: return u.format3->sanitize (SANITIZE_ARG);
498    default:return true;
499    }
500  }
501
502  private:
503  union {
504  USHORT		format;		/* Format identifier */
505  ContextFormat1	format1[];
506  ContextFormat2	format2[];
507  ContextFormat3	format3[];
508  } u;
509};
510ASSERT_SIZE (Context, 2);
511
512
513/* Chaining Contextual lookups */
514
515struct ChainContextLookupContext
516{
517  ContextFuncs funcs;
518  char *match_data[3];
519};
520
521static inline bool chain_context_lookup (APPLY_ARG_DEF,
522					 unsigned int backtrackCount,
523					 const USHORT backtrack[],
524					 unsigned int inputCount, /* Including the first glyph (not matched) */
525					 const USHORT input[], /* Array of input values--start with second glyph */
526					 unsigned int lookaheadCount,
527					 const USHORT lookahead[],
528					 unsigned int lookupCount,
529					 const LookupRecord lookupRecord[],
530					 ChainContextLookupContext &lookup_context)
531{
532  /* First guess */
533  if (HB_UNLIKELY (buffer->out_pos < backtrackCount ||
534		   buffer->in_pos + inputCount + lookaheadCount > buffer->in_length ||
535		   inputCount + lookaheadCount > context_length))
536    return false;
537
538  unsigned int offset;
539  return match_backtrack (APPLY_ARG,
540			  backtrackCount, backtrack,
541			  lookup_context.funcs.match, lookup_context.match_data[0]) &&
542	 match_input (APPLY_ARG,
543		      inputCount, input,
544		      lookup_context.funcs.match, lookup_context.match_data[1],
545		      &offset) &&
546	 match_lookahead (APPLY_ARG,
547			  lookaheadCount, lookahead,
548			  lookup_context.funcs.match, lookup_context.match_data[2],
549			  offset) &&
550	 (context_length = offset, true) &&
551	 apply_lookup (APPLY_ARG,
552		       inputCount,
553		       lookupCount, lookupRecord,
554		       lookup_context.funcs.apply);
555}
556
557struct ChainRule
558{
559  friend struct ChainRuleSet;
560
561  private:
562  inline bool apply (APPLY_ARG_DEF, ChainContextLookupContext &lookup_context) const
563  {
564    TRACE_APPLY ();
565    const HeadlessArrayOf<USHORT> &input = CONST_NEXT (HeadlessArrayOf<USHORT>, backtrack);
566    const ArrayOf<USHORT> &lookahead = CONST_NEXT (ArrayOf<USHORT>, input);
567    const ArrayOf<LookupRecord> &lookup = CONST_NEXT (ArrayOf<LookupRecord>, lookahead);
568    return chain_context_lookup (APPLY_ARG,
569				 backtrack.len, backtrack.array,
570				 input.len, input.array,
571				 lookahead.len, lookahead.array,
572				 lookup.len, lookup.array,
573				 lookup_context);
574    return false;
575  }
576
577  public:
578  inline bool sanitize (SANITIZE_ARG_DEF) {
579    TRACE_SANITIZE ();
580    if (!SANITIZE (backtrack)) return false;
581    HeadlessArrayOf<USHORT> &input = NEXT (HeadlessArrayOf<USHORT>, backtrack);
582    if (!SANITIZE (input)) return false;
583    ArrayOf<USHORT> &lookahead = NEXT (ArrayOf<USHORT>, input);
584    if (!SANITIZE (lookahead)) return false;
585    ArrayOf<LookupRecord> &lookup = NEXT (ArrayOf<LookupRecord>, lookahead);
586    return SANITIZE (lookup);
587  }
588
589  private:
590  ArrayOf<USHORT>
591		backtrack;		/* Array of backtracking values
592					 * (to be matched before the input
593					 * sequence) */
594  HeadlessArrayOf<USHORT>
595		inputX;			/* Array of input values (start with
596					 * second glyph) */
597  ArrayOf<USHORT>
598		lookaheadX;		/* Array of lookahead values's (to be
599					 * matched after the input sequence) */
600  ArrayOf<LookupRecord>
601		lookupX;		/* Array of LookupRecords--in
602					 * design order) */
603};
604ASSERT_SIZE (ChainRule, 8);
605
606struct ChainRuleSet
607{
608  inline bool apply (APPLY_ARG_DEF, ChainContextLookupContext &lookup_context) const
609  {
610    TRACE_APPLY ();
611    unsigned int num_rules = rule.len;
612    for (unsigned int i = 0; i < num_rules; i++)
613    {
614      if ((this+rule[i]).apply (APPLY_ARG, lookup_context))
615        return true;
616    }
617
618    return false;
619  }
620
621  inline bool sanitize (SANITIZE_ARG_DEF) {
622    TRACE_SANITIZE ();
623    return SANITIZE_THIS (rule);
624  }
625
626  private:
627  OffsetArrayOf<ChainRule>
628		rule;			/* Array of ChainRule tables
629					 * ordered by preference */
630};
631ASSERT_SIZE (ChainRuleSet, 2);
632
633struct ChainContextFormat1
634{
635  friend struct ChainContext;
636
637  private:
638  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
639  {
640    TRACE_APPLY ();
641    unsigned int index = (this+coverage) (IN_CURGLYPH ());
642    if (HB_LIKELY (index == NOT_COVERED))
643      return false;
644
645    const ChainRuleSet &rule_set = this+ruleSet[index];
646    struct ChainContextLookupContext lookup_context = {
647      {match_glyph, apply_func},
648      {NULL, NULL, NULL}
649    };
650    return rule_set.apply (APPLY_ARG, lookup_context);
651  }
652
653  inline bool sanitize (SANITIZE_ARG_DEF) {
654    TRACE_SANITIZE ();
655    return SANITIZE_THIS2 (coverage, ruleSet);
656  }
657
658  private:
659  USHORT	format;			/* Format identifier--format = 1 */
660  OffsetTo<Coverage>
661		coverage;		/* Offset to Coverage table--from
662					 * beginning of table */
663  OffsetArrayOf<ChainRuleSet>
664		ruleSet;		/* Array of ChainRuleSet tables
665					 * ordered by Coverage Index */
666};
667ASSERT_SIZE (ChainContextFormat1, 6);
668
669struct ChainContextFormat2
670{
671  friend struct ChainContext;
672
673  private:
674  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
675  {
676    TRACE_APPLY ();
677    unsigned int index = (this+coverage) (IN_CURGLYPH ());
678    if (HB_LIKELY (index == NOT_COVERED))
679      return false;
680
681    const ClassDef &backtrack_class_def = this+backtrackClassDef;
682    const ClassDef &input_class_def = this+inputClassDef;
683    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
684
685    index = input_class_def (IN_CURGLYPH ());
686    const ChainRuleSet &rule_set = this+ruleSet[index];
687    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
688     * them across subrule lookups.  Not sure it's worth it.
689     */
690    struct ChainContextLookupContext lookup_context = {
691     {match_class, apply_func},
692     {DECONST_CHARP(&backtrack_class_def),
693      DECONST_CHARP(&input_class_def),
694      DECONST_CHARP(&lookahead_class_def)}
695    };
696    return rule_set.apply (APPLY_ARG, lookup_context);
697  }
698
699  inline bool sanitize (SANITIZE_ARG_DEF) {
700    TRACE_SANITIZE ();
701    return SANITIZE_THIS2 (coverage, backtrackClassDef) &&
702	   SANITIZE_THIS2 (inputClassDef, lookaheadClassDef) &&
703	   SANITIZE_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 = CONST_NEXT (OffsetArrayOf<Coverage>, backtrack);
739
740    unsigned int index = (this+input[0]) (IN_CURGLYPH ());
741    if (HB_LIKELY (index == NOT_COVERED))
742      return false;
743
744    const OffsetArrayOf<Coverage> &lookahead = CONST_NEXT (OffsetArrayOf<Coverage>, input);
745    const ArrayOf<LookupRecord> &lookup = CONST_NEXT (ArrayOf<LookupRecord>, lookahead);
746    struct ChainContextLookupContext lookup_context = {
747      {match_coverage, apply_func},
748      {DECONST_CHARP(this), DECONST_CHARP(this), DECONST_CHARP(this)}
749    };
750    return chain_context_lookup (APPLY_ARG,
751				 backtrack.len, (USHORT *) backtrack.array,
752				 input.len, (USHORT *) input.array + 1,
753				 lookahead.len, (USHORT *) lookahead.array,
754				 lookup.len, lookup.array,
755				 lookup_context);
756    return false;
757  }
758
759  inline bool sanitize (SANITIZE_ARG_DEF) {
760    TRACE_SANITIZE ();
761    if (!SANITIZE_THIS (backtrack)) return false;
762    OffsetArrayOf<Coverage> &input = NEXT (OffsetArrayOf<Coverage>, backtrack);
763    if (!SANITIZE_THIS (input)) return false;
764    OffsetArrayOf<Coverage> &lookahead = NEXT (OffsetArrayOf<Coverage>, input);
765    if (!SANITIZE_THIS (lookahead)) return false;
766    ArrayOf<LookupRecord> &lookup = NEXT (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 (SANITIZE_ARG_DEF) {
805    TRACE_SANITIZE ();
806    if (!SANITIZE (u.format)) return false;
807    switch (u.format) {
808    case 1: return u.format1->sanitize (SANITIZE_ARG);
809    case 2: return u.format2->sanitize (SANITIZE_ARG);
810    case 3: return u.format3->sanitize (SANITIZE_ARG);
811    default:return true;
812    }
813  }
814
815  private:
816  union {
817  USHORT		format;	/* Format identifier */
818  ChainContextFormat1	format1[];
819  ChainContextFormat2	format2[];
820  ChainContextFormat3	format3[];
821  } u;
822};
823ASSERT_SIZE (ChainContext, 2);
824
825
826struct ExtensionFormat1
827{
828  friend struct Extension;
829
830  protected:
831  inline unsigned int get_type (void) const { return extensionLookupType; }
832  inline unsigned int get_offset (void) const { return (extensionOffset[0] << 16) + extensionOffset[1]; }
833  inline const LookupSubTable& get_subtable (void) const
834  {
835    unsigned int offset = get_offset ();
836    if (HB_UNLIKELY (!offset)) return Null(LookupSubTable);
837    return CONST_CAST (LookupSubTable, *this, offset);
838  }
839
840  inline bool sanitize (SANITIZE_ARG_DEF) {
841    TRACE_SANITIZE ();
842    return SANITIZE_SELF ();
843  }
844
845  private:
846  USHORT	format;			/* Format identifier. Set to 1. */
847  USHORT	extensionLookupType;	/* Lookup type of subtable referenced
848					 * by ExtensionOffset (i.e. the
849					 * extension subtable). */
850  USHORT	extensionOffset[2];	/* Offset to the extension subtable,
851					 * of lookup type subtable.
852					 * Defined as two shorts to avoid
853					 * alignment requirements. */
854};
855ASSERT_SIZE (ExtensionFormat1, 8);
856
857struct Extension
858{
859  inline unsigned int get_type (void) const
860  {
861    switch (u.format) {
862    case 1: return u.format1->get_type ();
863    default:return 0;
864    }
865  }
866  inline const LookupSubTable& get_subtable (void) const
867  {
868    switch (u.format) {
869    case 1: return u.format1->get_subtable ();
870    default:return Null(LookupSubTable);
871    }
872  }
873
874  inline bool sanitize (SANITIZE_ARG_DEF) {
875    TRACE_SANITIZE ();
876    if (!SANITIZE (u.format)) return false;
877    switch (u.format) {
878    case 1: return u.format1->sanitize (SANITIZE_ARG);
879    default:return true;
880    }
881  }
882
883  private:
884  union {
885  USHORT		format;		/* Format identifier */
886  ExtensionFormat1	format1[];
887  } u;
888};
889ASSERT_SIZE (Extension, 2);
890
891
892/*
893 * GSUB/GPOS Common
894 */
895
896struct GSUBGPOS
897{
898  static const hb_tag_t GSUBTag	= HB_OT_TAG_GSUB;
899  static const hb_tag_t GPOSTag	= HB_OT_TAG_GPOS;
900
901  STATIC_DEFINE_GET_FOR_DATA_CHECK_MAJOR_VERSION (GSUBGPOS, 1, 1);
902
903  inline unsigned int get_script_count (void) const
904  { return (this+scriptList).len; }
905  inline const Tag& get_script_tag (unsigned int i) const
906  { return (this+scriptList).get_tag (i); }
907  inline bool get_script_tags (unsigned int *script_count /* IN/OUT */,
908			       hb_tag_t     *script_tags /* OUT */) const
909  { return (this+scriptList).get_tags (script_count, script_tags); }
910  inline const Script& get_script (unsigned int i) const
911  { return (this+scriptList)[i]; }
912  inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
913  { return (this+scriptList).find_index (tag, index); }
914
915  inline unsigned int get_feature_count (void) const
916  { return (this+featureList).len; }
917  inline const Tag& get_feature_tag (unsigned int i) const
918  { return (this+featureList).get_tag (i); }
919  inline bool get_feature_tags (unsigned int *feature_count /* IN/OUT */,
920				hb_tag_t     *feature_tags /* OUT */) const
921  { return (this+featureList).get_tags (feature_count, feature_tags); }
922  inline const Feature& get_feature (unsigned int i) const
923  { return (this+featureList)[i]; }
924  inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const
925  { return (this+featureList).find_index (tag, index); }
926
927  inline unsigned int get_lookup_count (void) const
928  { return (this+lookupList).len; }
929  inline const Lookup& get_lookup (unsigned int i) const
930  { return (this+lookupList)[i]; }
931
932  inline bool sanitize (SANITIZE_ARG_DEF) {
933    TRACE_SANITIZE ();
934    if (!SANITIZE (version)) return false;
935    if (version.major != 1) return true;
936    return SANITIZE_THIS3 (scriptList, featureList, lookupList);
937  }
938
939  protected:
940  FixedVersion	version;	/* Version of the GSUB/GPOS table--initially set
941				 * to 0x00010000 */
942  OffsetTo<ScriptList>
943		scriptList;  	/* ScriptList table */
944  OffsetTo<FeatureList>
945		featureList; 	/* FeatureList table */
946  OffsetTo<LookupList>
947		lookupList; 	/* LookupList table */
948};
949ASSERT_SIZE (GSUBGPOS, 10);
950
951
952#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
953