hb-ot-layout-gsubgpos-private.hh revision a328d66e6a8122f7d4d71941449d4d0136203e08
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    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_CAST (LookupRecord, input, sizeof (input[0]) * (inputCount ? inputCount - 1 : 0));
260    return context_lookup (APPLY_ARG,
261			   inputCount, input,
262			   lookupCount, lookupRecord,
263			   lookup_context);
264  }
265
266  public:
267  inline bool sanitize (SANITIZE_ARG_DEF) {
268    if (!SANITIZE_SELF ()) return false;
269    return SANITIZE_MEM (input,
270			 sizeof (input[0]) * inputCount +
271			 sizeof (lookupRecordX[0]) * lookupCount);
272  }
273
274  private:
275  USHORT	inputCount;		/* Total number of glyphs in input
276					 * glyph sequence--includes the  first
277					 * glyph */
278  USHORT	lookupCount;		/* Number of LookupRecords */
279  USHORT	input[];		/* Array of match inputs--start with
280					 * second glyph */
281  LookupRecord	lookupRecordX[];	/* Array of LookupRecords--in
282					 * design order */
283};
284ASSERT_SIZE (Rule, 4);
285
286struct RuleSet
287{
288  inline bool apply (APPLY_ARG_DEF, ContextLookupContext &lookup_context) const
289  {
290    unsigned int num_rules = rule.len;
291    for (unsigned int i = 0; i < num_rules; i++)
292    {
293      if ((this+rule[i]).apply (APPLY_ARG, lookup_context))
294        return true;
295    }
296
297    return false;
298  }
299
300  inline bool sanitize (SANITIZE_ARG_DEF) {
301    return SANITIZE_THIS (rule);
302  }
303
304  private:
305  OffsetArrayOf<Rule>
306		rule;			/* Array of Rule tables
307					 * ordered by preference */
308};
309
310
311struct ContextFormat1
312{
313  friend struct Context;
314
315  private:
316  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
317  {
318    unsigned int index = (this+coverage) (IN_CURGLYPH ());
319    if (HB_LIKELY (index == NOT_COVERED))
320      return false;
321
322    const RuleSet &rule_set = this+ruleSet[index];
323    struct ContextLookupContext lookup_context = {
324      {match_glyph, apply_func},
325      NULL
326    };
327    return rule_set.apply (APPLY_ARG, lookup_context);
328  }
329
330  inline bool sanitize (SANITIZE_ARG_DEF) {
331    return SANITIZE_THIS2 (coverage, ruleSet);
332  }
333
334  private:
335  USHORT	format;			/* Format identifier--format = 1 */
336  OffsetTo<Coverage>
337		coverage;		/* Offset to Coverage table--from
338					 * beginning of table */
339  OffsetArrayOf<RuleSet>
340		ruleSet;		/* Array of RuleSet tables
341					 * ordered by Coverage Index */
342};
343ASSERT_SIZE (ContextFormat1, 6);
344
345
346struct ContextFormat2
347{
348  friend struct Context;
349
350  private:
351  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
352  {
353    unsigned int index = (this+coverage) (IN_CURGLYPH ());
354    if (HB_LIKELY (index == NOT_COVERED))
355      return false;
356
357    const ClassDef &class_def = this+classDef;
358    index = class_def (IN_CURGLYPH ());
359    const RuleSet &rule_set = this+ruleSet[index];
360    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
361     * them across subrule lookups.  Not sure it's worth it.
362     */
363    struct ContextLookupContext lookup_context = {
364     {match_class, apply_func},
365      DECONST_CHARP(&class_def)
366    };
367    return rule_set.apply (APPLY_ARG, lookup_context);
368  }
369
370  inline bool sanitize (SANITIZE_ARG_DEF) {
371    return SANITIZE_THIS3 (coverage, classDef, ruleSet);
372  }
373
374  private:
375  USHORT	format;			/* Format identifier--format = 2 */
376  OffsetTo<Coverage>
377		coverage;		/* Offset to Coverage table--from
378					 * beginning of table */
379  OffsetTo<ClassDef>
380		classDef;		/* Offset to glyph ClassDef table--from
381					 * beginning of table */
382  OffsetArrayOf<RuleSet>
383		ruleSet;		/* Array of RuleSet tables
384					 * ordered by class */
385};
386ASSERT_SIZE (ContextFormat2, 8);
387
388
389struct ContextFormat3
390{
391  friend struct Context;
392
393  private:
394  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
395  {
396    unsigned int index = (this+coverage[0]) (IN_CURGLYPH ());
397    if (HB_LIKELY (index == NOT_COVERED))
398      return false;
399
400    const LookupRecord *lookupRecord = &CONST_CAST(LookupRecord, coverage, sizeof (coverage[0]) * glyphCount);
401    struct ContextLookupContext lookup_context = {
402      {match_coverage, apply_func},
403      DECONST_CHARP(this)
404    };
405    return context_lookup (APPLY_ARG,
406			   glyphCount, (const USHORT *) (coverage + 1),
407			   lookupCount, lookupRecord,
408			   lookup_context);
409  }
410
411  inline bool sanitize (SANITIZE_ARG_DEF) {
412    if (!SANITIZE_SELF ()) return false;
413    unsigned int count = glyphCount;
414    for (unsigned int i = 0; i < count; i++)
415      if (!SANITIZE_THIS (coverage[i])) return false;
416    LookupRecord *lookupRecord = &CAST(LookupRecord, coverage, sizeof (coverage[0]) * glyphCount);
417    return SANITIZE_MEM (lookupRecord, sizeof (lookupRecord[0]) * lookupCount);
418  }
419
420  private:
421  USHORT	format;			/* Format identifier--format = 3 */
422  USHORT	glyphCount;		/* Number of glyphs in the input glyph
423					 * sequence */
424  USHORT	lookupCount;		/* Number of LookupRecords */
425  OffsetTo<Coverage>
426		coverage[];		/* Array of offsets to Coverage
427					 * table in glyph sequence order */
428  LookupRecord	lookupRecordX[];	/* Array of LookupRecords--in
429					 * design order */
430};
431ASSERT_SIZE (ContextFormat3, 6);
432
433struct Context
434{
435  protected:
436  bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
437  {
438    switch (u.format) {
439    case 1: return u.format1->apply (APPLY_ARG, apply_func);
440    case 2: return u.format2->apply (APPLY_ARG, apply_func);
441    case 3: return u.format3->apply (APPLY_ARG, apply_func);
442    default:return false;
443    }
444  }
445
446  inline bool sanitize (SANITIZE_ARG_DEF) {
447    if (!SANITIZE (u.format)) return false;
448    switch (u.format) {
449    case 1: return u.format1->sanitize (SANITIZE_ARG);
450    case 2: return u.format2->sanitize (SANITIZE_ARG);
451    case 3: return u.format3->sanitize (SANITIZE_ARG);
452    default:return true;
453    }
454  }
455
456  private:
457  union {
458  USHORT		format;		/* Format identifier */
459  ContextFormat1	format1[];
460  ContextFormat2	format2[];
461  ContextFormat3	format3[];
462  } u;
463};
464ASSERT_SIZE (Context, 2);
465
466
467/* Chaining Contextual lookups */
468
469struct ChainContextLookupContext
470{
471  ContextFuncs funcs;
472  char *match_data[3];
473};
474
475static inline bool chain_context_lookup (APPLY_ARG_DEF,
476					 unsigned int backtrackCount,
477					 const USHORT backtrack[],
478					 unsigned int inputCount, /* Including the first glyph (not matched) */
479					 const USHORT input[], /* Array of input values--start with second glyph */
480					 unsigned int lookaheadCount,
481					 const USHORT lookahead[],
482					 unsigned int lookupCount,
483					 const LookupRecord lookupRecord[],
484					 ChainContextLookupContext &lookup_context)
485{
486  /* First guess */
487  if (HB_UNLIKELY (buffer->out_pos < backtrackCount ||
488		   buffer->in_pos + inputCount + lookaheadCount > buffer->in_length ||
489		   inputCount + lookaheadCount > context_length))
490    return false;
491
492  unsigned int offset;
493  return match_backtrack (APPLY_ARG,
494			  backtrackCount, backtrack,
495			  lookup_context.funcs.match, lookup_context.match_data[0]) &&
496	 match_input (APPLY_ARG,
497		      inputCount, input,
498		      lookup_context.funcs.match, lookup_context.match_data[1],
499		      &offset) &&
500	 match_lookahead (APPLY_ARG,
501			  lookaheadCount, lookahead,
502			  lookup_context.funcs.match, lookup_context.match_data[2],
503			  offset) &&
504	 (context_length = offset, true) &&
505	 apply_lookup (APPLY_ARG,
506		       inputCount,
507		       lookupCount, lookupRecord,
508		       lookup_context.funcs.apply);
509}
510
511struct ChainRule
512{
513  friend struct ChainRuleSet;
514
515  private:
516  inline bool apply (APPLY_ARG_DEF, ChainContextLookupContext &lookup_context) const
517  {
518    const HeadlessArrayOf<USHORT> &input = CONST_CAST (HeadlessArrayOf<USHORT>, backtrack, backtrack.get_size ());
519    const ArrayOf<USHORT> &lookahead = CONST_CAST (ArrayOf<USHORT>, input, input.get_size ());
520    const ArrayOf<LookupRecord> &lookup = CONST_CAST (ArrayOf<LookupRecord>, lookahead, lookahead.get_size ());
521    return chain_context_lookup (APPLY_ARG,
522				 backtrack.len, backtrack.array,
523				 input.len, input.array + 1,
524				 lookahead.len, lookahead.array,
525				 lookup.len, lookup.array,
526				 lookup_context);
527    return false;
528  }
529
530  public:
531  inline bool sanitize (SANITIZE_ARG_DEF) {
532    if (!SANITIZE (backtrack)) return false;
533    HeadlessArrayOf<USHORT> &input = CAST (HeadlessArrayOf<USHORT>, backtrack, backtrack.get_size ());
534    if (!SANITIZE (input)) return false;
535    ArrayOf<USHORT> &lookahead = CAST (ArrayOf<USHORT>, input, input.get_size ());
536    if (!SANITIZE (lookahead)) return false;
537    ArrayOf<LookupRecord> &lookup = CAST (ArrayOf<LookupRecord>, lookahead, lookahead.get_size ());
538    return SANITIZE (lookup);
539  }
540
541  private:
542  ArrayOf<USHORT>
543		backtrack;		/* Array of backtracking values
544					 * (to be matched before the input
545					 * sequence) */
546  HeadlessArrayOf<USHORT>
547		inputX;			/* Array of input values (start with
548					 * second glyph) */
549  ArrayOf<USHORT>
550		lookaheadX;		/* Array of lookahead values's (to be
551					 * matched after the input sequence) */
552  ArrayOf<LookupRecord>
553		lookupX;		/* Array of LookupRecords--in
554					 * design order) */
555};
556ASSERT_SIZE (ChainRule, 8);
557
558struct ChainRuleSet
559{
560  inline bool apply (APPLY_ARG_DEF, ChainContextLookupContext &lookup_context) const
561  {
562    unsigned int num_rules = rule.len;
563    for (unsigned int i = 0; i < num_rules; i++)
564    {
565      if ((this+rule[i]).apply (APPLY_ARG, lookup_context))
566        return true;
567    }
568
569    return false;
570  }
571
572  inline bool sanitize (SANITIZE_ARG_DEF) {
573    return SANITIZE_THIS (rule);
574  }
575
576  private:
577  OffsetArrayOf<ChainRule>
578		rule;			/* Array of ChainRule tables
579					 * ordered by preference */
580};
581ASSERT_SIZE (ChainRuleSet, 2);
582
583struct ChainContextFormat1
584{
585  friend struct ChainContext;
586
587  private:
588  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
589  {
590    unsigned int index = (this+coverage) (IN_CURGLYPH ());
591    if (HB_LIKELY (index == NOT_COVERED))
592      return false;
593
594    const ChainRuleSet &rule_set = this+ruleSet[index];
595    struct ChainContextLookupContext lookup_context = {
596      {match_glyph, apply_func},
597      {NULL, NULL, NULL}
598    };
599    return rule_set.apply (APPLY_ARG, lookup_context);
600  }
601
602  inline bool sanitize (SANITIZE_ARG_DEF) {
603    return SANITIZE_THIS2 (coverage, ruleSet);
604  }
605
606  private:
607  USHORT	format;			/* Format identifier--format = 1 */
608  OffsetTo<Coverage>
609		coverage;		/* Offset to Coverage table--from
610					 * beginning of table */
611  OffsetArrayOf<ChainRuleSet>
612		ruleSet;		/* Array of ChainRuleSet tables
613					 * ordered by Coverage Index */
614};
615ASSERT_SIZE (ChainContextFormat1, 6);
616
617struct ChainContextFormat2
618{
619  friend struct ChainContext;
620
621  private:
622  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
623  {
624    unsigned int index = (this+coverage) (IN_CURGLYPH ());
625    if (HB_LIKELY (index == NOT_COVERED))
626      return false;
627
628    const ClassDef &backtrack_class_def = this+backtrackClassDef;
629    const ClassDef &input_class_def = this+inputClassDef;
630    const ClassDef &lookahead_class_def = this+lookaheadClassDef;
631
632    index = input_class_def (IN_CURGLYPH ());
633    const ChainRuleSet &rule_set = this+ruleSet[index];
634    /* LONGTERMTODO: Old code fetches glyph classes at most once and caches
635     * them across subrule lookups.  Not sure it's worth it.
636     */
637    struct ChainContextLookupContext lookup_context = {
638     {match_class, apply_func},
639     {DECONST_CHARP(&backtrack_class_def),
640      DECONST_CHARP(&input_class_def),
641      DECONST_CHARP(&lookahead_class_def)}
642    };
643    return rule_set.apply (APPLY_ARG, lookup_context);
644  }
645
646  inline bool sanitize (SANITIZE_ARG_DEF) {
647    return SANITIZE_THIS2 (coverage, backtrackClassDef) &&
648	   SANITIZE_THIS2 (inputClassDef, lookaheadClassDef) &&
649	   SANITIZE_THIS (ruleSet);
650  }
651
652  private:
653  USHORT	format;			/* Format identifier--format = 2 */
654  OffsetTo<Coverage>
655		coverage;		/* Offset to Coverage table--from
656					 * beginning of table */
657  OffsetTo<ClassDef>
658		backtrackClassDef;	/* Offset to glyph ClassDef table
659					 * containing backtrack sequence
660					 * data--from beginning of table */
661  OffsetTo<ClassDef>
662		inputClassDef;		/* Offset to glyph ClassDef
663					 * table containing input sequence
664					 * data--from beginning of table */
665  OffsetTo<ClassDef>
666		lookaheadClassDef;	/* Offset to glyph ClassDef table
667					 * containing lookahead sequence
668					 * data--from beginning of table */
669  OffsetArrayOf<ChainRuleSet>
670		ruleSet;		/* Array of ChainRuleSet tables
671					 * ordered by class */
672};
673ASSERT_SIZE (ChainContextFormat2, 12);
674
675struct ChainContextFormat3
676{
677  friend struct ChainContext;
678
679  private:
680
681  inline bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
682  {
683    const OffsetArrayOf<Coverage> &input = CONST_CAST (OffsetArrayOf<Coverage>, backtrack, backtrack.get_size ());
684
685    unsigned int index = (this+input[0]) (IN_CURGLYPH ());
686    if (HB_LIKELY (index == NOT_COVERED))
687      return false;
688
689    const OffsetArrayOf<Coverage> &lookahead = CONST_CAST (OffsetArrayOf<Coverage>, input, input.get_size ());
690    const ArrayOf<LookupRecord> &lookup = CONST_CAST (ArrayOf<LookupRecord>, lookahead, lookahead.get_size ());
691    struct ChainContextLookupContext lookup_context = {
692      {match_coverage, apply_func},
693      {DECONST_CHARP(this), DECONST_CHARP(this), DECONST_CHARP(this)}
694    };
695    return chain_context_lookup (APPLY_ARG,
696				 backtrack.len, (USHORT *) backtrack.array,
697				 input.len, (USHORT *) input.array,
698				 lookahead.len, (USHORT *) lookahead.array,
699				 lookup.len, lookup.array,
700				 lookup_context);
701    return false;
702  }
703
704  inline bool sanitize (SANITIZE_ARG_DEF) {
705    if (!SANITIZE_THIS (backtrack)) return false;
706    OffsetArrayOf<Coverage> &input = CAST (OffsetArrayOf<Coverage>, backtrack, backtrack.get_size ());
707    if (!SANITIZE_THIS (input)) return false;
708    OffsetArrayOf<Coverage> &lookahead = CAST (OffsetArrayOf<Coverage>, input, input.get_size ());
709    if (!SANITIZE_THIS (lookahead)) return false;
710    ArrayOf<LookupRecord> &lookup = CAST (ArrayOf<LookupRecord>, lookahead, lookahead.get_size ());
711    return SANITIZE (lookup);
712  }
713
714  private:
715  USHORT	format;			/* Format identifier--format = 3 */
716  OffsetArrayOf<Coverage>
717		backtrack;		/* Array of coverage tables
718					 * in backtracking sequence, in  glyph
719					 * sequence order */
720  OffsetArrayOf<Coverage>
721		inputX		;	/* Array of coverage
722					 * tables in input sequence, in glyph
723					 * sequence order */
724  OffsetArrayOf<Coverage>
725		lookaheadX;		/* Array of coverage tables
726					 * in lookahead sequence, in glyph
727					 * sequence order */
728  ArrayOf<LookupRecord>
729		lookupX;		/* Array of LookupRecords--in
730					 * design order) */
731};
732ASSERT_SIZE (ChainContextFormat3, 10);
733
734struct ChainContext
735{
736  protected:
737  bool apply (APPLY_ARG_DEF, apply_lookup_func_t apply_func) const
738  {
739    switch (u.format) {
740    case 1: return u.format1->apply (APPLY_ARG, apply_func);
741    case 2: return u.format2->apply (APPLY_ARG, apply_func);
742    case 3: return u.format3->apply (APPLY_ARG, apply_func);
743    default:return false;
744    }
745  }
746
747  inline bool sanitize (SANITIZE_ARG_DEF) {
748    if (!SANITIZE (u.format)) return false;
749    switch (u.format) {
750    case 1: return u.format1->sanitize (SANITIZE_ARG);
751    case 2: return u.format2->sanitize (SANITIZE_ARG);
752    case 3: return u.format3->sanitize (SANITIZE_ARG);
753    default:return true;
754    }
755  }
756
757  private:
758  union {
759  USHORT		format;	/* Format identifier */
760  ChainContextFormat1	format1[];
761  ChainContextFormat2	format2[];
762  ChainContextFormat3	format3[];
763  } u;
764};
765ASSERT_SIZE (ChainContext, 2);
766
767
768struct ExtensionFormat1
769{
770  friend struct Extension;
771
772  protected:
773  inline unsigned int get_type (void) const { return extensionLookupType; }
774  inline unsigned int get_offset (void) const { return (extensionOffset[0] << 16) + extensionOffset[1]; }
775  inline const LookupSubTable& get_subtable (void) const
776  {
777    unsigned int offset = get_offset ();
778    if (HB_UNLIKELY (!offset)) return Null(LookupSubTable);
779    return CONST_CAST (LookupSubTable, *this, offset);
780  }
781
782  inline bool sanitize (SANITIZE_ARG_DEF) {
783    return SANITIZE_SELF ();
784  }
785
786  private:
787  USHORT	format;			/* Format identifier. Set to 1. */
788  USHORT	extensionLookupType;	/* Lookup type of subtable referenced
789					 * by ExtensionOffset (i.e. the
790					 * extension subtable). */
791  USHORT	extensionOffset[2];	/* Offset to the extension subtable,
792					 * of lookup type subtable.
793					 * Defined as two shorts to avoid
794					 * alignment requirements. */
795};
796ASSERT_SIZE (ExtensionFormat1, 8);
797
798struct Extension
799{
800  inline unsigned int get_type (void) const
801  {
802    switch (u.format) {
803    case 1: return u.format1->get_type ();
804    default:return 0;
805    }
806  }
807  inline const LookupSubTable& get_subtable (void) const
808  {
809    switch (u.format) {
810    case 1: return u.format1->get_subtable ();
811    default:return Null(LookupSubTable);
812    }
813  }
814
815  inline bool sanitize (SANITIZE_ARG_DEF) {
816    if (!SANITIZE (u.format)) return false;
817    switch (u.format) {
818    case 1: return u.format1->sanitize (SANITIZE_ARG);
819    default:return true;
820    }
821  }
822
823  private:
824  union {
825  USHORT		format;		/* Format identifier */
826  ExtensionFormat1	format1[];
827  } u;
828};
829ASSERT_SIZE (Extension, 2);
830
831
832/*
833 * GSUB/GPOS Common
834 */
835
836struct GSUBGPOS
837{
838  static const hb_tag_t GSUBTag	= HB_OT_TAG_GSUB;
839  static const hb_tag_t GPOSTag	= HB_OT_TAG_GPOS;
840
841  STATIC_DEFINE_GET_FOR_DATA_CHECK_MAJOR_VERSION (GSUBGPOS, 1, 1);
842
843  DEFINE_TAG_LIST_INTERFACE (Script,  script );	/* get_script_count (), get_script (i), get_script_tag (i) */
844  DEFINE_TAG_LIST_INTERFACE (Feature, feature);	/* get_feature_count(), get_feature(i), get_feature_tag(i) */
845  DEFINE_LIST_INTERFACE     (Lookup,  lookup );	/* get_lookup_count (), get_lookup (i) */
846
847  // LONGTERMTODO bsearch
848  DEFINE_TAG_FIND_INTERFACE (Script,  script );	/* find_script_index (), get_script_by_tag (tag) */
849  DEFINE_TAG_FIND_INTERFACE (Feature, feature);	/* find_feature_index(), get_feature_by_tag(tag) */
850
851  bool sanitize (SANITIZE_ARG_DEF) {
852    if (!SANITIZE (version)) return false;
853    if (version.major != 1) return true;
854    return SANITIZE_THIS3 (scriptList, featureList, lookupList);
855  }
856
857  protected:
858  FixedVersion	version;	/* Version of the GSUB/GPOS table--initially set
859				 * to 0x00010000 */
860  OffsetTo<ScriptList>
861		scriptList;  	/* ScriptList table */
862  OffsetTo<FeatureList>
863		featureList; 	/* FeatureList table */
864  OffsetTo<LookupList>
865		lookupList; 	/* LookupList table */
866};
867ASSERT_SIZE (GSUBGPOS, 10);
868
869
870#endif /* HB_OT_LAYOUT_GSUBGPOS_PRIVATE_HH */
871