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