2 * Copyright © 2017 Google, Inc.
4 * This is part of HarfBuzz, a text shaping library.
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.
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
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.
24 * Google Author(s): Behdad Esfahbod
27 #ifndef HB_AAT_LAYOUT_COMMON_PRIVATE_HH
28 #define HB_AAT_LAYOUT_COMMON_PRIVATE_HH
30 #include "hb-aat-layout-private.hh"
39 * Binary Searching Tables
42 struct BinSearchHeader
45 inline bool sanitize (hb_sanitize_context_t *c) const
47 TRACE_SANITIZE (this);
48 return_trace (c->check_struct (this));
51 HBUINT16 unitSize; /* Size of a lookup unit for this search in bytes. */
52 HBUINT16 nUnits; /* Number of units of the preceding size to be searched. */
53 HBUINT16 searchRange; /* The value of unitSize times the largest power of 2
54 * that is less than or equal to the value of nUnits. */
55 HBUINT16 entrySelector; /* The log base 2 of the largest power of 2 less than
56 * or equal to the value of nUnits. */
57 HBUINT16 rangeShift; /* The value of unitSize times the difference of the
58 * value of nUnits minus the largest power of 2 less
59 * than or equal to the value of nUnits. */
61 DEFINE_SIZE_STATIC (10);
64 template <typename Type>
65 struct BinSearchArrayOf
67 inline const Type& operator [] (unsigned int i) const
69 if (unlikely (i >= header.nUnits)) return Null(Type);
70 return StructAtOffset<Type> (bytes, i * header.unitSize);
72 inline Type& operator [] (unsigned int i)
74 return StructAtOffset<Type> (bytes, i * header.unitSize);
76 inline unsigned int get_size (void) const
77 { return header.static_size + header.nUnits * header.unitSize; }
79 inline bool sanitize (hb_sanitize_context_t *c) const
81 TRACE_SANITIZE (this);
82 if (unlikely (!sanitize_shallow (c))) return_trace (false);
84 /* Note: for structs that do not reference other structs,
85 * we do not need to call their sanitize() as we already did
86 * a bound check on the aggregate array size. We just include
87 * a small unreachable expression to make sure the structs
88 * pointed to do have a simple sanitize(), ie. they do not
89 * reference other structs via offsets.
91 (void) (false && StructAtOffset<Type> (bytes, 0).sanitize (c));
95 inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
97 TRACE_SANITIZE (this);
98 if (unlikely (!sanitize_shallow (c))) return_trace (false);
99 unsigned int count = header.nUnits;
100 for (unsigned int i = 0; i < count; i++)
101 if (unlikely (!(*this)[i].sanitize (c, base)))
102 return_trace (false);
106 template <typename T>
107 inline const Type *bsearch (const T &key) const
109 unsigned int size = header.unitSize;
110 int min = 0, max = (int) header.nUnits - 1;
113 int mid = (min + max) / 2;
114 const Type *p = (const Type *) (((const char *) bytes) + (mid * size));
115 int c = p->cmp (key);
127 inline bool sanitize_shallow (hb_sanitize_context_t *c) const
129 TRACE_SANITIZE (this);
130 return_trace (header.sanitize (c) &&
131 Type::static_size >= header.unitSize &&
132 c->check_array (bytes, header.unitSize, header.nUnits));
136 BinSearchHeader header;
139 DEFINE_SIZE_ARRAY (10, bytes);
143 /* TODO Move this to hb-open-type-private.hh and use it in ArrayOf, HeadlessArrayOf,
144 * and other places around the code base?? */
145 template <typename Type>
146 struct UnsizedArrayOf
148 inline const Type& operator [] (unsigned int i) const { return arrayZ[i]; }
149 inline Type& operator [] (unsigned int i) { return arrayZ[i]; }
151 inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
153 TRACE_SANITIZE (this);
154 if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
156 /* Note: for structs that do not reference other structs,
157 * we do not need to call their sanitize() as we already did
158 * a bound check on the aggregate array size. We just include
159 * a small unreachable expression to make sure the structs
160 * pointed to do have a simple sanitize(), ie. they do not
161 * reference other structs via offsets.
163 (void) (false && arrayZ[0].sanitize (c));
167 inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base) const
169 TRACE_SANITIZE (this);
170 if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
171 for (unsigned int i = 0; i < count; i++)
172 if (unlikely (!arrayZ[i].sanitize (c, base)))
173 return_trace (false);
176 template <typename T>
177 inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, const void *base, T user_data) const
179 TRACE_SANITIZE (this);
180 if (unlikely (!sanitize_shallow (c, count))) return_trace (false);
181 for (unsigned int i = 0; i < count; i++)
182 if (unlikely (!arrayZ[i].sanitize (c, base, user_data)))
183 return_trace (false);
188 inline bool sanitize_shallow (hb_sanitize_context_t *c, unsigned int count) const
190 TRACE_SANITIZE (this);
191 return_trace (c->check_array (arrayZ, arrayZ[0].static_size, count));
197 DEFINE_SIZE_ARRAY (0, arrayZ);
200 /* Unsized array of offset's */
201 template <typename Type, typename OffsetType>
202 struct UnsizedOffsetArrayOf : UnsizedArrayOf<OffsetTo<Type, OffsetType> > {};
204 /* Unsized array of offsets relative to the beginning of the array itself. */
205 template <typename Type, typename OffsetType>
206 struct UnsizedOffsetListOf : UnsizedOffsetArrayOf<Type, OffsetType>
208 inline const Type& operator [] (unsigned int i) const
210 return this+this->arrayZ[i];
213 inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
215 TRACE_SANITIZE (this);
216 return_trace ((UnsizedOffsetArrayOf<Type, OffsetType>::sanitize (c, count, this)));
218 template <typename T>
219 inline bool sanitize (hb_sanitize_context_t *c, unsigned int count, T user_data) const
221 TRACE_SANITIZE (this);
222 return_trace ((UnsizedOffsetArrayOf<Type, OffsetType>::sanitize (c, count, this, user_data)));
231 template <typename T> struct Lookup;
233 template <typename T>
236 friend struct Lookup<T>;
239 inline const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
241 if (unlikely (glyph_id >= num_glyphs)) return nullptr;
242 return &arrayZ[glyph_id];
245 inline bool sanitize (hb_sanitize_context_t *c) const
247 TRACE_SANITIZE (this);
248 return_trace (arrayZ.sanitize (c, c->num_glyphs));
252 HBUINT16 format; /* Format identifier--format = 0 */
254 arrayZ; /* Array of lookup values, indexed by glyph index. */
256 DEFINE_SIZE_ARRAY (2, arrayZ);
260 template <typename T>
261 struct LookupSegmentSingle
263 inline int cmp (hb_codepoint_t g) const {
264 return g < first ? -1 : g <= last ? 0 : +1 ;
267 inline bool sanitize (hb_sanitize_context_t *c) const
269 TRACE_SANITIZE (this);
270 return_trace (c->check_struct (this) && value.sanitize (c));
273 GlyphID last; /* Last GlyphID in this segment */
274 GlyphID first; /* First GlyphID in this segment */
275 T value; /* The lookup value (only one) */
277 DEFINE_SIZE_STATIC (4 + T::static_size);
280 template <typename T>
283 friend struct Lookup<T>;
286 inline const T* get_value (hb_codepoint_t glyph_id) const
288 const LookupSegmentSingle<T> *v = segments.bsearch (glyph_id);
289 return v ? &v->value : nullptr;
292 inline bool sanitize (hb_sanitize_context_t *c) const
294 TRACE_SANITIZE (this);
295 return_trace (segments.sanitize (c));
299 HBUINT16 format; /* Format identifier--format = 2 */
300 BinSearchArrayOf<LookupSegmentSingle<T> >
301 segments; /* The actual segments. These must already be sorted,
302 * according to the first word in each one (the last
303 * glyph in each segment). */
305 DEFINE_SIZE_ARRAY (8, segments);
308 template <typename T>
309 struct LookupSegmentArray
311 inline const T* get_value (hb_codepoint_t glyph_id, const void *base) const
313 return first <= glyph_id && glyph_id <= last ? &(base+valuesZ)[glyph_id - first] : nullptr;
316 inline int cmp (hb_codepoint_t g) const {
317 return g < first ? -1 : g <= last ? 0 : +1 ;
320 inline bool sanitize (hb_sanitize_context_t *c, const void *base) const
322 TRACE_SANITIZE (this);
323 return_trace (c->check_struct (this) &&
325 valuesZ.sanitize (c, base, last - first + 1));
328 GlyphID last; /* Last GlyphID in this segment */
329 GlyphID first; /* First GlyphID in this segment */
330 OffsetTo<UnsizedArrayOf<T> >
331 valuesZ; /* A 16-bit offset from the start of
332 * the table to the data. */
334 DEFINE_SIZE_STATIC (6);
337 template <typename T>
340 friend struct Lookup<T>;
343 inline const T* get_value (hb_codepoint_t glyph_id) const
345 const LookupSegmentArray<T> *v = segments.bsearch (glyph_id);
346 return v ? v->get_value (glyph_id, this) : nullptr;
349 inline bool sanitize (hb_sanitize_context_t *c) const
351 TRACE_SANITIZE (this);
352 return_trace (segments.sanitize (c, this));
356 HBUINT16 format; /* Format identifier--format = 2 */
357 BinSearchArrayOf<LookupSegmentArray<T> >
358 segments; /* The actual segments. These must already be sorted,
359 * according to the first word in each one (the last
360 * glyph in each segment). */
362 DEFINE_SIZE_ARRAY (8, segments);
365 template <typename T>
368 inline int cmp (hb_codepoint_t g) const { return glyph.cmp (g); }
370 inline bool sanitize (hb_sanitize_context_t *c) const
372 TRACE_SANITIZE (this);
373 return_trace (c->check_struct (this) && value.sanitize (c));
376 GlyphID glyph; /* Last GlyphID */
377 T value; /* The lookup value (only one) */
379 DEFINE_SIZE_STATIC (4 + T::static_size);
382 template <typename T>
385 friend struct Lookup<T>;
388 inline const T* get_value (hb_codepoint_t glyph_id) const
390 const LookupSingle<T> *v = entries.bsearch (glyph_id);
391 return v ? &v->value : nullptr;
394 inline bool sanitize (hb_sanitize_context_t *c) const
396 TRACE_SANITIZE (this);
397 return_trace (entries.sanitize (c));
401 HBUINT16 format; /* Format identifier--format = 6 */
402 BinSearchArrayOf<LookupSingle<T> >
403 entries; /* The actual entries, sorted by glyph index. */
405 DEFINE_SIZE_ARRAY (8, entries);
408 template <typename T>
411 friend struct Lookup<T>;
414 inline const T* get_value (hb_codepoint_t glyph_id) const
416 return firstGlyph <= glyph_id && glyph_id - firstGlyph < glyphCount ? &valueArrayZ[glyph_id - firstGlyph] : nullptr;
419 inline bool sanitize (hb_sanitize_context_t *c) const
421 TRACE_SANITIZE (this);
422 return_trace (c->check_struct (this) && valueArrayZ.sanitize (c, glyphCount));
426 HBUINT16 format; /* Format identifier--format = 6 */
427 GlyphID firstGlyph; /* First glyph index included in the trimmed array. */
428 HBUINT16 glyphCount; /* Total number of glyphs (equivalent to the last
429 * glyph minus the value of firstGlyph plus 1). */
431 valueArrayZ; /* The lookup values (indexed by the glyph index
432 * minus the value of firstGlyph). */
434 DEFINE_SIZE_ARRAY (6, valueArrayZ);
437 template <typename T>
440 inline const T* get_value (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
443 case 0: return u.format0.get_value (glyph_id, num_glyphs);
444 case 2: return u.format2.get_value (glyph_id);
445 case 4: return u.format4.get_value (glyph_id);
446 case 6: return u.format6.get_value (glyph_id);
447 case 8: return u.format8.get_value (glyph_id);
448 default:return nullptr;
452 inline bool sanitize (hb_sanitize_context_t *c) const
454 TRACE_SANITIZE (this);
455 if (!u.format.sanitize (c)) return_trace (false);
457 case 0: return_trace (u.format0.sanitize (c));
458 case 2: return_trace (u.format2.sanitize (c));
459 case 4: return_trace (u.format4.sanitize (c));
460 case 6: return_trace (u.format6.sanitize (c));
461 case 8: return_trace (u.format8.sanitize (c));
462 default:return_trace (true);
468 HBUINT16 format; /* Format identifier */
469 LookupFormat0<T> format0;
470 LookupFormat2<T> format2;
471 LookupFormat4<T> format4;
472 LookupFormat6<T> format6;
473 LookupFormat8<T> format8;
476 DEFINE_SIZE_UNION (2, format);
481 * Extended State Table
484 template <typename T>
487 inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
489 TRACE_SANITIZE (this);
490 /* Note, we don't recurse-sanitize data because we don't access it.
491 * That said, in our DEFINE_SIZE_STATIC we access T::static_size,
492 * which ensures that data has a simple sanitize(). To be determined
493 * if I need to remove that as well. */
494 return_trace (c->check_struct (this));
498 HBUINT16 newState; /* Byte offset from beginning of state table
499 * to the new state. Really?!?! Or just state
500 * number? The latter in morx for sure. */
501 HBUINT16 flags; /* Table specific. */
502 T data; /* Optional offsets to per-glyph tables. */
504 DEFINE_SIZE_STATIC (4 + T::static_size);
510 inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const
512 TRACE_SANITIZE (this);
513 return_trace (c->check_struct (this));
517 HBUINT16 newState; /* Byte offset from beginning of state table to the new state. */
518 HBUINT16 flags; /* Table specific. */
520 DEFINE_SIZE_STATIC (4);
523 template <typename Extra>
526 inline unsigned int get_class (hb_codepoint_t glyph_id, unsigned int num_glyphs) const
528 const HBUINT16 *v = (this+classTable).get_value (glyph_id, num_glyphs);
532 inline const Entry<Extra> *get_entries () const
534 return (this+entryTable).arrayZ;
537 inline const Entry<Extra> *get_entryZ (unsigned int state, unsigned int klass) const
539 if (unlikely (klass >= nClasses)) return nullptr;
541 const HBUINT16 *states = (this+stateArrayTable).arrayZ;
542 const Entry<Extra> *entries = (this+entryTable).arrayZ;
544 unsigned int entry = states[state * nClasses + klass];
546 return &entries[entry];
549 inline bool sanitize (hb_sanitize_context_t *c,
550 unsigned int *num_entries_out = nullptr) const
552 TRACE_SANITIZE (this);
553 if (unlikely (!(c->check_struct (this) &&
554 classTable.sanitize (c, this)))) return_trace (false);
556 const HBUINT16 *states = (this+stateArrayTable).arrayZ;
557 const Entry<Extra> *entries = (this+entryTable).arrayZ;
559 unsigned int num_states = 1;
560 unsigned int num_entries = 0;
562 unsigned int state = 0;
563 unsigned int entry = 0;
564 while (state < num_states)
566 if (unlikely (!c->check_array (states,
567 states[0].static_size * nClasses,
569 return_trace (false);
570 { /* Sweep new states. */
571 const HBUINT16 *stop = &states[num_states * nClasses];
572 for (const HBUINT16 *p = &states[state * nClasses]; p < stop; p++)
573 num_entries = MAX<unsigned int> (num_entries, *p + 1);
577 if (unlikely (!c->check_array (entries,
578 entries[0].static_size,
580 return_trace (false);
581 { /* Sweep new entries. */
582 const Entry<Extra> *stop = &entries[num_entries];
583 for (const Entry<Extra> *p = &entries[entry]; p < stop; p++)
584 num_states = MAX<unsigned int> (num_states, p->newState + 1);
590 *num_entries_out = num_entries;
596 HBUINT32 nClasses; /* Number of classes, which is the number of indices
597 * in a single line in the state array. */
598 OffsetTo<Lookup<HBUINT16>, HBUINT32>
599 classTable; /* Offset to the class table. */
600 OffsetTo<UnsizedArrayOf<HBUINT16>, HBUINT32>
601 stateArrayTable;/* Offset to the state array. */
602 OffsetTo<UnsizedArrayOf<Entry<Extra> >, HBUINT32>
603 entryTable; /* Offset to the entry array. */
606 DEFINE_SIZE_STATIC (16);
609 template <typename EntryData>
610 struct StateTableDriver
612 inline StateTableDriver (const StateTable<EntryData> &machine_,
613 hb_buffer_t *buffer_,
617 num_glyphs (face_->get_num_glyphs ()) {}
619 template <typename context_t>
620 inline void drive (context_t *c)
622 hb_glyph_info_t *info = buffer->info;
625 buffer->clear_output ();
627 unsigned int state = 0;
628 bool last_was_dont_advance = false;
629 for (buffer->idx = 0;;)
631 unsigned int klass = buffer->idx < buffer->len ?
632 machine.get_class (info[buffer->idx].codepoint, num_glyphs) :
634 const Entry<EntryData> *entry = machine.get_entryZ (state, klass);
635 if (unlikely (!entry))
638 /* Unsafe-to-break before this if not in state 0, as things might
639 * go differently if we start from state 0 here. */
640 if (state && buffer->idx)
642 /* If there's no action and we're just epsilon-transitioning to state 0,
644 if (c->is_actionable (this, entry) ||
645 !(entry->newState == 0 && entry->flags == context_t::DontAdvance))
646 buffer->unsafe_to_break (buffer->idx - 1, buffer->idx + 1);
649 /* Unsafe-to-break if end-of-text would kick in here. */
650 if (buffer->idx + 2 <= buffer->len)
652 const Entry<EntryData> *end_entry = machine.get_entryZ (state, 0);
653 if (c->is_actionable (this, end_entry))
654 buffer->unsafe_to_break (buffer->idx, buffer->idx + 2);
657 if (unlikely (!c->transition (this, entry)))
660 last_was_dont_advance = (entry->flags & context_t::DontAdvance) && buffer->max_ops-- > 0;
662 state = entry->newState;
664 if (buffer->idx == buffer->len)
667 if (!last_was_dont_advance)
668 buffer->next_glyph ();
673 for (; buffer->idx < buffer->len;)
674 buffer->next_glyph ();
675 buffer->swap_buffers ();
680 const StateTable<EntryData> &machine;
682 unsigned int num_glyphs;
687 struct hb_aat_apply_context_t :
688 hb_dispatch_context_t<hb_aat_apply_context_t, bool, HB_DEBUG_APPLY>
690 inline const char *get_name (void) { return "APPLY"; }
691 template <typename T>
692 inline return_t dispatch (const T &obj) { return obj.apply (this); }
693 static return_t default_return_value (void) { return false; }
694 bool stop_sublookup_iteration (return_t r) const { return r; }
699 hb_sanitize_context_t sanitizer;
701 /* Unused. For debug tracing only. */
702 unsigned int lookup_index;
703 unsigned int debug_depth;
705 inline hb_aat_apply_context_t (hb_font_t *font_,
706 hb_buffer_t *buffer_,
708 font (font_), face (font->face), buffer (buffer_),
709 sanitizer (), lookup_index (0), debug_depth (0)
711 sanitizer.init (table);
712 sanitizer.num_glyphs = face->get_num_glyphs ();
713 sanitizer.start_processing ();
716 inline void set_lookup_index (unsigned int i) { lookup_index = i; }
718 inline ~hb_aat_apply_context_t (void)
720 sanitizer.end_processing ();
725 } /* namespace AAT */
728 #endif /* HB_AAT_LAYOUT_COMMON_PRIVATE_HH */