hb-ot-layout-gsubgpos-private.hh revision 284899ccbe79fda7405ab09d3092fc25fd89e810
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 = *reinterpret_cast<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    SANITIZE_DEBUG ();
167    return SANITIZE_SELF ();
168  }
169
170  USHORT	sequenceIndex;		/* Index into current glyph
171					 * sequence--first glyph = 0 */
172  USHORT	lookupListIndex;	/* Lookup to apply to that
173					 * position--zero--based */
174};
175ASSERT_SIZE (LookupRecord, 4);
176
177static inline bool apply_lookup (APPLY_ARG_DEF,
178				 unsigned int count, /* Including the first glyph */
179				 unsigned int lookupCount,
180				 const LookupRecord lookupRecord[], /* Array of LookupRecords--in design order */
181				 apply_lookup_func_t apply_func)
182{
183  unsigned int end = MIN (buffer->in_length, buffer->in_pos + context_length);
184  if (HB_UNLIKELY (buffer->in_pos + count > end))
185    return false;
186
187  /* TODO We don't support lookupRecord arrays that are not increasing:
188   *      Should be easy for in_place ones at least. */
189  for (unsigned int i = 0; i < count; i++)
190  {
191    while (_hb_ot_layout_skip_mark (context->face, IN_CURINFO (), lookup_flag, NULL))
192    {
193      if (HB_UNLIKELY (buffer->in_pos == end))
194	return true;
195      /* No lookup applied for this index */
196      _hb_buffer_next_glyph (buffer);
197    }
198
199    if (lookupCount && i == lookupRecord->sequenceIndex)
200    {
201      unsigned int old_pos = buffer->in_pos;
202
203      /* Apply a lookup */
204      bool done = apply_func (APPLY_ARG, lookupRecord->lookupListIndex);
205
206      lookupRecord++;
207      lookupCount--;
208      i += buffer->in_pos - old_pos;
209      if (HB_UNLIKELY (buffer->in_pos == end))
210	return true;
211
212      if (!done)
213	goto not_applied;
214    }
215    else
216    {
217    not_applied:
218      /* No lookup applied for this index */
219      _hb_buffer_next_glyph (buffer);
220      i++;
221    }
222  }
223
224  return true;
225}
226
227
228/* Contextual lookups */
229
230struct ContextLookupContext
231{
232  ContextFuncs funcs;
233  char *match_data;
234};
235
236static inline bool context_lookup (APPLY_ARG_DEF,
237				   unsigned int inputCount, /* Including the first glyph (not matched) */
238				   const USHORT input[], /* Array of input values--start with second glyph */
239				   unsigned int lookupCount,
240				   const LookupRecord lookupRecord[],
241				   ContextLookupContext &lookup_context)
242{
243  return match_input (APPLY_ARG,
244		      inputCount, input,
245		      lookup_context.funcs.match, lookup_context.match_data,
246		      &context_length) &&
247	 apply_lookup (APPLY_ARG,
248		       inputCount,
249		       lookupCount, lookupRecord,
250		       lookup_context.funcs.apply);
251}
252
253struct Rule
254{
255  friend struct RuleSet;
256
257  private:
258  inline bool apply (APPLY_ARG_DEF, ContextLookupContext &lookup_context) const
259  {
260    const LookupRecord *lookupRecord = &CONST_CAST (LookupRecord, input, sizeof (input[0]) * (inputCount ? inputCount - 1 : 0));
261    return context_lookup (APPLY_ARG,
262			   inputCount, input,
263			   lookupCount, lookupRecord,
264			   lookup_context);
265  }
266
267  public:
268  inline bool sanitize (SANITIZE_ARG_DEF) {
269    SANITIZE_DEBUG ();
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    SANITIZE_DEBUG ();
304    return SANITIZE_THIS (rule);
305  }
306
307  private:
308  OffsetArrayOf<Rule>
309		rule;			/* Array of Rule tables
310					 * ordered by preference */
311};
312
313
314struct ContextFormat1
315{
316  friend struct Context;
317
318  private:
319  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
320  {
321    unsigned int index = (this+coverage) (IN_CURGLYPH ());
322    if (HB_LIKELY (index == NOT_COVERED))
323      return false;
324
325    const RuleSet &rule_set = this+ruleSet[index];
326    struct ContextLookupContext lookup_context = {
327      {match_glyph, apply_func},
328      NULL
329    };
330    return rule_set.apply (APPLY_ARG, lookup_context);
331  }
332
333  inline bool sanitize (SANITIZE_ARG_DEF) {
334    SANITIZE_DEBUG ();
335    return SANITIZE_THIS2 (coverage, ruleSet);
336  }
337
338  private:
339  USHORT	format;			/* Format identifier--format = 1 */
340  OffsetTo<Coverage>
341		coverage;		/* Offset to Coverage table--from
342					 * beginning of table */
343  OffsetArrayOf<RuleSet>
344		ruleSet;		/* Array of RuleSet tables
345					 * ordered by Coverage Index */
346};
347ASSERT_SIZE (ContextFormat1, 6);
348
349
350struct ContextFormat2
351{
352  friend struct Context;
353
354  private:
355  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
356  {
357    unsigned int index = (this+coverage) (IN_CURGLYPH ());
358    if (HB_LIKELY (index == NOT_COVERED))
359      return false;
360
361    const ClassDef &class_def = this+classDef;
362    index = class_def (IN_CURGLYPH ());
363    const RuleSet &rule_set = this+ruleSet[index];
364    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
365     * them across subrule lookups.  Not sure it's worth it.
366     */
367    struct ContextLookupContext lookup_context = {
368     {match_class, apply_func},
369      DECONST_CHARP(&class_def)
370    };
371    return rule_set.apply (APPLY_ARG, lookup_context);
372  }
373
374  inline bool sanitize (SANITIZE_ARG_DEF) {
375    SANITIZE_DEBUG ();
376    return SANITIZE_THIS3 (coverage, classDef, ruleSet);
377  }
378
379  private:
380  USHORT	format;			/* Format identifier--format = 2 */
381  OffsetTo<Coverage>
382		coverage;		/* Offset to Coverage table--from
383					 * beginning of table */
384  OffsetTo<ClassDef>
385		classDef;		/* Offset to glyph ClassDef table--from
386					 * beginning of table */
387  OffsetArrayOf<RuleSet>
388		ruleSet;		/* Array of RuleSet tables
389					 * ordered by class */
390};
391ASSERT_SIZE (ContextFormat2, 8);
392
393
394struct ContextFormat3
395{
396  friend struct Context;
397
398  private:
399  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
400  {
401    unsigned int index = (this+coverage[0]) (IN_CURGLYPH ());
402    if (HB_LIKELY (index == NOT_COVERED))
403      return false;
404
405    const LookupRecord *lookupRecord = &CONST_CAST(LookupRecord, coverage, sizeof (coverage[0]) * glyphCount);
406    struct ContextLookupContext lookup_context = {
407      {match_coverage, apply_func},
408      DECONST_CHARP(this)
409    };
410    return context_lookup (APPLY_ARG,
411			   glyphCount, (const USHORT *) (coverage + 1),
412			   lookupCount, lookupRecord,
413			   lookup_context);
414  }
415
416  inline bool sanitize (SANITIZE_ARG_DEF) {
417    SANITIZE_DEBUG ();
418    if (!SANITIZE_SELF ()) return false;
419    unsigned int count = glyphCount;
420    for (unsigned int i = 0; i < count; i++)
421      if (!SANITIZE_THIS (coverage[i])) return false;
422    LookupRecord *lookupRecord = &CAST(LookupRecord, coverage, 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  bool sanitize (SANITIZE_ARG_DEF) {
453    SANITIZE_DEBUG ();
454    if (!SANITIZE (u.format)) return false;
455    switch (u.format) {
456    case 1: return u.format1->sanitize (SANITIZE_ARG);
457    case 2: return u.format2->sanitize (SANITIZE_ARG);
458    case 3: return u.format3->sanitize (SANITIZE_ARG);
459    default:return true;
460    }
461  }
462
463  private:
464  union {
465  USHORT		format;		/* Format identifier */
466  ContextFormat1	format1[];
467  ContextFormat2	format2[];
468  ContextFormat3	format3[];
469  } u;
470};
471ASSERT_SIZE (Context, 2);
472
473
474/* Chaining Contextual lookups */
475
476struct ChainContextLookupContext
477{
478  ContextFuncs funcs;
479  char *match_data[3];
480};
481
482static inline bool chain_context_lookup (APPLY_ARG_DEF,
483					 unsigned int backtrackCount,
484					 const USHORT backtrack[],
485					 unsigned int inputCount, /* Including the first glyph (not matched) */
486					 const USHORT input[], /* Array of input values--start with second glyph */
487					 unsigned int lookaheadCount,
488					 const USHORT lookahead[],
489					 unsigned int lookupCount,
490					 const LookupRecord lookupRecord[],
491					 ChainContextLookupContext &lookup_context)
492{
493  /* First guess */
494  if (HB_UNLIKELY (buffer->out_pos < backtrackCount ||
495		   buffer->in_pos + inputCount + lookaheadCount > buffer->in_length ||
496		   inputCount + lookaheadCount > context_length))
497    return false;
498
499  unsigned int offset;
500  return match_backtrack (APPLY_ARG,
501			  backtrackCount, backtrack,
502			  lookup_context.funcs.match, lookup_context.match_data[0]) &&
503	 match_input (APPLY_ARG,
504		      inputCount, input,
505		      lookup_context.funcs.match, lookup_context.match_data[1],
506		      &offset) &&
507	 match_lookahead (APPLY_ARG,
508			  lookaheadCount, lookahead,
509			  lookup_context.funcs.match, lookup_context.match_data[2],
510			  offset) &&
511	 (context_length = offset, true) &&
512	 apply_lookup (APPLY_ARG,
513		       inputCount,
514		       lookupCount, lookupRecord,
515		       lookup_context.funcs.apply);
516}
517
518struct ChainRule
519{
520  friend struct ChainRuleSet;
521
522  private:
523  inline bool apply (APPLY_ARG_DEF, ChainContextLookupContext &lookup_context) const
524  {
525    const HeadlessArrayOf<USHORT> &input = CONST_NEXT (HeadlessArrayOf<USHORT>, backtrack);
526    const ArrayOf<USHORT> &lookahead = CONST_NEXT (ArrayOf<USHORT>, input);
527    const ArrayOf<LookupRecord> &lookup = CONST_NEXT (ArrayOf<LookupRecord>, lookahead);
528    return chain_context_lookup (APPLY_ARG,
529				 backtrack.len, backtrack.array,
530				 input.len, input.array + 1,
531				 lookahead.len, lookahead.array,
532				 lookup.len, lookup.array,
533				 lookup_context);
534    return false;
535  }
536
537  public:
538  inline bool sanitize (SANITIZE_ARG_DEF) {
539    SANITIZE_DEBUG ();
540    if (!SANITIZE (backtrack)) return false;
541    HeadlessArrayOf<USHORT> &input = NEXT (HeadlessArrayOf<USHORT>, backtrack);
542    if (!SANITIZE (input)) return false;
543    ArrayOf<USHORT> &lookahead = NEXT (ArrayOf<USHORT>, input);
544    if (!SANITIZE (lookahead)) return false;
545    ArrayOf<LookupRecord> &lookup = NEXT (ArrayOf<LookupRecord>, lookahead);
546    return SANITIZE (lookup);
547  }
548
549  private:
550  ArrayOf<USHORT>
551		backtrack;		/* Array of backtracking values
552					 * (to be matched before the input
553					 * sequence) */
554  HeadlessArrayOf<USHORT>
555		inputX;			/* Array of input values (start with
556					 * second glyph) */
557  ArrayOf<USHORT>
558		lookaheadX;		/* Array of lookahead values's (to be
559					 * matched after the input sequence) */
560  ArrayOf<LookupRecord>
561		lookupX;		/* Array of LookupRecords--in
562					 * design order) */
563};
564ASSERT_SIZE (ChainRule, 8);
565
566struct ChainRuleSet
567{
568  inline bool apply (APPLY_ARG_DEF, ChainContextLookupContext &lookup_context) const
569  {
570    unsigned int num_rules = rule.len;
571    for (unsigned int i = 0; i < num_rules; i++)
572    {
573      if ((this+rule[i]).apply (APPLY_ARG, lookup_context))
574        return true;
575    }
576
577    return false;
578  }
579
580  inline bool sanitize (SANITIZE_ARG_DEF) {
581    SANITIZE_DEBUG ();
582    return SANITIZE_THIS (rule);
583  }
584
585  private:
586  OffsetArrayOf<ChainRule>
587		rule;			/* Array of ChainRule tables
588					 * ordered by preference */
589};
590ASSERT_SIZE (ChainRuleSet, 2);
591
592struct ChainContextFormat1
593{
594  friend struct ChainContext;
595
596  private:
597  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
598  {
599    unsigned int index = (this+coverage) (IN_CURGLYPH ());
600    if (HB_LIKELY (index == NOT_COVERED))
601      return false;
602
603    const ChainRuleSet &rule_set = this+ruleSet[index];
604    struct ChainContextLookupContext lookup_context = {
605      {match_glyph, apply_func},
606      {NULL, NULL, NULL}
607    };
608    return rule_set.apply (APPLY_ARG, lookup_context);
609  }
610
611  inline bool sanitize (SANITIZE_ARG_DEF) {
612    SANITIZE_DEBUG ();
613    return SANITIZE_THIS2 (coverage, ruleSet);
614  }
615
616  private:
617  USHORT	format;			/* Format identifier--format = 1 */
618  OffsetTo<Coverage>
619		coverage;		/* Offset to Coverage table--from
620					 * beginning of table */
621  OffsetArrayOf<ChainRuleSet>
622		ruleSet;		/* Array of ChainRuleSet tables
623					 * ordered by Coverage Index */
624};
625ASSERT_SIZE (ChainContextFormat1, 6);
626
627struct ChainContextFormat2
628{
629  friend struct ChainContext;
630
631  private:
632  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
633  {
634    unsigned int index = (this+coverage) (IN_CURGLYPH ());
635    if (HB_LIKELY (index == NOT_COVERED))
636      return false;
637
638    const ClassDef &backtrack_class_def = this+backtrackClassDef;
639    const ClassDef &input_class_def = this+inputClassDef;
640    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
641
642    index = input_class_def (IN_CURGLYPH ());
643    const ChainRuleSet &rule_set = this+ruleSet[index];
644    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
645     * them across subrule lookups.  Not sure it's worth it.
646     */
647    struct ChainContextLookupContext lookup_context = {
648     {match_class, apply_func},
649     {DECONST_CHARP(&backtrack_class_def),
650      DECONST_CHARP(&input_class_def),
651      DECONST_CHARP(&lookahead_class_def)}
652    };
653    return rule_set.apply (APPLY_ARG, lookup_context);
654  }
655
656  inline bool sanitize (SANITIZE_ARG_DEF) {
657    SANITIZE_DEBUG ();
658    return SANITIZE_THIS2 (coverage, backtrackClassDef) &&
659	   SANITIZE_THIS2 (inputClassDef, lookaheadClassDef) &&
660	   SANITIZE_THIS (ruleSet);
661  }
662
663  private:
664  USHORT	format;			/* Format identifier--format = 2 */
665  OffsetTo<Coverage>
666		coverage;		/* Offset to Coverage table--from
667					 * beginning of table */
668  OffsetTo<ClassDef>
669		backtrackClassDef;	/* Offset to glyph ClassDef table
670					 * containing backtrack sequence
671					 * data--from beginning of table */
672  OffsetTo<ClassDef>
673		inputClassDef;		/* Offset to glyph ClassDef
674					 * table containing input sequence
675					 * data--from beginning of table */
676  OffsetTo<ClassDef>
677		lookaheadClassDef;	/* Offset to glyph ClassDef table
678					 * containing lookahead sequence
679					 * data--from beginning of table */
680  OffsetArrayOf<ChainRuleSet>
681		ruleSet;		/* Array of ChainRuleSet tables
682					 * ordered by class */
683};
684ASSERT_SIZE (ChainContextFormat2, 12);
685
686struct ChainContextFormat3
687{
688  friend struct ChainContext;
689
690  private:
691
692  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
693  {
694    const OffsetArrayOf<Coverage> &input = CONST_NEXT (OffsetArrayOf<Coverage>, backtrack);
695
696    unsigned int index = (this+input[0]) (IN_CURGLYPH ());
697    if (HB_LIKELY (index == NOT_COVERED))
698      return false;
699
700    const OffsetArrayOf<Coverage> &lookahead = CONST_NEXT (OffsetArrayOf<Coverage>, input);
701    const ArrayOf<LookupRecord> &lookup = CONST_NEXT (ArrayOf<LookupRecord>, lookahead);
702    struct ChainContextLookupContext lookup_context = {
703      {match_coverage, apply_func},
704      {DECONST_CHARP(this), DECONST_CHARP(this), DECONST_CHARP(this)}
705    };
706    return chain_context_lookup (APPLY_ARG,
707				 backtrack.len, (USHORT *) backtrack.array,
708				 input.len, (USHORT *) input.array,
709				 lookahead.len, (USHORT *) lookahead.array,
710				 lookup.len, lookup.array,
711				 lookup_context);
712    return false;
713  }
714
715  inline bool sanitize (SANITIZE_ARG_DEF) {
716    SANITIZE_DEBUG ();
717    if (!SANITIZE_THIS (backtrack)) return false;
718    OffsetArrayOf<Coverage> &input = NEXT (OffsetArrayOf<Coverage>, backtrack);
719    if (!SANITIZE_THIS (input)) return false;
720    OffsetArrayOf<Coverage> &lookahead = NEXT (OffsetArrayOf<Coverage>, input);
721    if (!SANITIZE_THIS (lookahead)) return false;
722    ArrayOf<LookupRecord> &lookup = NEXT (ArrayOf<LookupRecord>, lookahead);
723    return SANITIZE (lookup);
724  }
725
726  private:
727  USHORT	format;			/* Format identifier--format = 3 */
728  OffsetArrayOf<Coverage>
729		backtrack;		/* Array of coverage tables
730					 * in backtracking sequence, in  glyph
731					 * sequence order */
732  OffsetArrayOf<Coverage>
733		inputX		;	/* Array of coverage
734					 * tables in input sequence, in glyph
735					 * sequence order */
736  OffsetArrayOf<Coverage>
737		lookaheadX;		/* Array of coverage tables
738					 * in lookahead sequence, in glyph
739					 * sequence order */
740  ArrayOf<LookupRecord>
741		lookupX;		/* Array of LookupRecords--in
742					 * design order) */
743};
744ASSERT_SIZE (ChainContextFormat3, 10);
745
746struct ChainContext
747{
748  protected:
749  bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
750  {
751    switch (u.format) {
752    case 1: return u.format1->apply (APPLY_ARG, apply_func);
753    case 2: return u.format2->apply (APPLY_ARG, apply_func);
754    case 3: return u.format3->apply (APPLY_ARG, apply_func);
755    default:return false;
756    }
757  }
758
759  bool sanitize (SANITIZE_ARG_DEF) {
760    SANITIZE_DEBUG ();
761    if (!SANITIZE (u.format)) return false;
762    switch (u.format) {
763    case 1: return u.format1->sanitize (SANITIZE_ARG);
764    case 2: return u.format2->sanitize (SANITIZE_ARG);
765    case 3: return u.format3->sanitize (SANITIZE_ARG);
766    default:return true;
767    }
768  }
769
770  private:
771  union {
772  USHORT		format;	/* Format identifier */
773  ChainContextFormat1	format1[];
774  ChainContextFormat2	format2[];
775  ChainContextFormat3	format3[];
776  } u;
777};
778ASSERT_SIZE (ChainContext, 2);
779
780
781struct ExtensionFormat1
782{
783  friend struct Extension;
784
785  protected:
786  inline unsigned int get_type (void) const { return extensionLookupType; }
787  inline unsigned int get_offset (void) const { return (extensionOffset[0] << 16) + extensionOffset[1]; }
788  inline const LookupSubTable& get_subtable (void) const
789  {
790    unsigned int offset = get_offset ();
791    if (HB_UNLIKELY (!offset)) return Null(LookupSubTable);
792    return CONST_CAST (LookupSubTable, *this, offset);
793  }
794
795  inline bool sanitize (SANITIZE_ARG_DEF) {
796    SANITIZE_DEBUG ();
797    return SANITIZE_SELF ();
798  }
799
800  private:
801  USHORT	format;			/* Format identifier. Set to 1. */
802  USHORT	extensionLookupType;	/* Lookup type of subtable referenced
803					 * by ExtensionOffset (i.e. the
804					 * extension subtable). */
805  USHORT	extensionOffset[2];	/* Offset to the extension subtable,
806					 * of lookup type subtable.
807					 * Defined as two shorts to avoid
808					 * alignment requirements. */
809};
810ASSERT_SIZE (ExtensionFormat1, 8);
811
812struct Extension
813{
814  inline unsigned int get_type (void) const
815  {
816    switch (u.format) {
817    case 1: return u.format1->get_type ();
818    default:return 0;
819    }
820  }
821  inline const LookupSubTable& get_subtable (void) const
822  {
823    switch (u.format) {
824    case 1: return u.format1->get_subtable ();
825    default:return Null(LookupSubTable);
826    }
827  }
828
829  bool sanitize (SANITIZE_ARG_DEF) {
830    SANITIZE_DEBUG ();
831    if (!SANITIZE (u.format)) return false;
832    switch (u.format) {
833    case 1: return u.format1->sanitize (SANITIZE_ARG);
834    default:return true;
835    }
836  }
837
838  private:
839  union {
840  USHORT		format;		/* Format identifier */
841  ExtensionFormat1	format1[];
842  } u;
843};
844ASSERT_SIZE (Extension, 2);
845
846
847/*
848 * GSUB/GPOS Common
849 */
850
851struct GSUBGPOS
852{
853  static const hb_tag_t GSUBTag	= HB_OT_TAG_GSUB;
854  static const hb_tag_t GPOSTag	= HB_OT_TAG_GPOS;
855
856  STATIC_DEFINE_GET_FOR_DATA_CHECK_MAJOR_VERSION (GSUBGPOS, 1, 1);
857
858  inline unsigned int get_script_count (void) const
859  { return (this+scriptList).len; }
860  inline const Tag& get_script_tag (unsigned int i) const
861  { return (this+scriptList).get_tag (i); }
862  inline bool get_script_tags (unsigned int *script_count /* IN/OUT */,
863			       hb_tag_t     *script_tags /* OUT */) const
864  { return (this+scriptList).get_tags (script_count, script_tags); }
865  inline const Script& get_script (unsigned int i) const
866  { return (this+scriptList)[i]; }
867  inline bool find_script_index (hb_tag_t tag, unsigned int *index) const
868  { return (this+scriptList).find_index (tag, index); }
869
870  inline unsigned int get_feature_count (void) const
871  { return (this+featureList).len; }
872  inline const Tag& get_feature_tag (unsigned int i) const
873  { return (this+featureList).get_tag (i); }
874  inline bool get_feature_tags (unsigned int *feature_count /* IN/OUT */,
875				hb_tag_t     *feature_tags /* OUT */) const
876  { return (this+featureList).get_tags (feature_count, feature_tags); }
877  inline const Feature& get_feature (unsigned int i) const
878  { return (this+featureList)[i]; }
879  inline bool find_feature_index (hb_tag_t tag, unsigned int *index) const
880  { return (this+featureList).find_index (tag, index); }
881
882  inline unsigned int get_lookup_count (void) const
883  { return (this+lookupList).len; }
884  inline const Lookup& get_lookup (unsigned int i) const
885  { return (this+lookupList)[i]; }
886
887  bool sanitize (SANITIZE_ARG_DEF) {
888    SANITIZE_DEBUG ();
889    if (!SANITIZE (version)) return false;
890    if (version.major != 1) return true;
891    return SANITIZE_THIS3 (scriptList, featureList, lookupList);
892  }
893
894  protected:
895  FixedVersion	version;	/* Version of the GSUB/GPOS table--initially set
896				 * to 0x00010000 */
897  OffsetTo<ScriptList>
898		scriptList;  	/* ScriptList table */
899  OffsetTo<FeatureList>
900		featureList; 	/* FeatureList table */
901  OffsetTo<LookupList>
902		lookupList; 	/* LookupList table */
903};
904ASSERT_SIZE (GSUBGPOS, 10);
905
906
907#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
908