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