hb-open-type-private.hh revision 2c9fd2adce5a6a9dcd62c874bd64613ea68d8d9b
1/*
2 * Copyright (C) 2007,2008,2009,2010  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_OPEN_TYPES_PRIVATE_HH
28#define HB_OPEN_TYPES_PRIVATE_HH
29
30#include "hb-private.h"
31
32#include "hb-blob.h"
33
34
35#define NO_INDEX		((unsigned int) 0xFFFF)
36
37
38/*
39 * Casts
40 */
41
42#define CONST_CHARP(X)		(reinterpret_cast<const char *>(X))
43#define DECONST_CHARP(X)	((char *)reinterpret_cast<const char *>(X))
44#define CHARP(X)		(reinterpret_cast<char *>(X))
45
46#define CONST_CAST(T,X,Ofs)	(*(reinterpret_cast<const T *>(CONST_CHARP(&(X)) + Ofs)))
47#define DECONST_CAST(T,X,Ofs)	(*(reinterpret_cast<T *>((char *)CONST_CHARP(&(X)) + Ofs)))
48#define CAST(T,X,Ofs) 		(*(reinterpret_cast<T *>(CHARP(&(X)) + Ofs)))
49
50#define CONST_NEXT(T,X)		(*(reinterpret_cast<const T *>(CONST_CHARP(&(X)) + (X).get_size ())))
51#define NEXT(T,X)		(*(reinterpret_cast<T *>(CHARP(&(X)) + (X).get_size ())))
52
53#define CONST_ARRAY_AFTER(T,X)	((reinterpret_cast<const T *>(CONST_CHARP(&(X)) + X.get_size ())))
54#define ARRAY_AFTER(T,X)	((reinterpret_cast<T *>(CHARP(&(X)) + X.get_size ())))
55
56/*
57 * Class features
58 */
59
60
61/* Null objects */
62
63/* Global nul-content Null pool.  Enlarge as necessary. */
64static const void *_NullPool[32 / sizeof (void *)];
65
66/* Generic template for nul-content sizeof-sized Null objects. */
67template <typename Type>
68static inline const Type& Null () {
69  ASSERT_STATIC (sizeof (Type) <= sizeof (_NullPool));
70  return CONST_CAST (Type, *_NullPool, 0);
71}
72
73/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */
74#define DEFINE_NULL_DATA(Type, size, data) \
75static const char _Null##Type[size + 1] = data; \
76template <> \
77inline const Type& Null<Type> () { \
78  return CONST_CAST (Type, *_Null##Type, 0); \
79}
80
81/* Accessor macro. */
82#define Null(Type) Null<Type>()
83
84
85/* get_for_data() is a static class method returning a reference to an
86 * instance of Type located at the input data location.  It's just a
87 * fancy, NULL-safe, cast! */
88#define STATIC_DEFINE_GET_FOR_DATA(Type) \
89  static inline const Type& get_for_data (const char *data) \
90  { \
91    if (HB_UNLIKELY (data == NULL)) return Null(Type); \
92    return CONST_CAST (Type, *data, 0); \
93  }
94/* Like get_for_data(), but checks major version first. */
95#define STATIC_DEFINE_GET_FOR_DATA_CHECK_MAJOR_VERSION(Type, MajorMin, MajorMax) \
96  static inline const Type& get_for_data (const char *data) \
97  { \
98    if (HB_UNLIKELY (data == NULL)) return Null(Type); \
99    const Type& t = CONST_CAST (Type, *data, 0); \
100    if (HB_UNLIKELY (t.version.major < MajorMin || t.version.major > MajorMax)) return Null(Type); \
101    return t; \
102  }
103
104
105/*
106 * Sanitize
107 */
108
109#ifndef HB_DEBUG_SANITIZE
110#define HB_DEBUG_SANITIZE HB_DEBUG
111#endif
112
113#if HB_DEBUG_SANITIZE
114#include <stdio.h>
115#define TRACE_SANITIZE_ARG_DEF	, unsigned int sanitize_depth HB_GNUC_UNUSED
116#define TRACE_SANITIZE_ARG	, sanitize_depth + 1
117#define TRACE_SANITIZE_ARG_INIT	, 1
118#define TRACE_SANITIZE() \
119	HB_STMT_START { \
120	    if (sanitize_depth < HB_DEBUG_SANITIZE) \
121		fprintf (stderr, "SANITIZE(%p) %-*d-> %s\n", \
122			 (CONST_CHARP (this) == CONST_CHARP (&NullPool)) ? 0 : this, \
123			 sanitize_depth, sanitize_depth, \
124			 __PRETTY_FUNCTION__); \
125	} HB_STMT_END
126#else
127#define TRACE_SANITIZE_ARG_DEF
128#define TRACE_SANITIZE_ARG
129#define TRACE_SANITIZE_ARG_INIT
130#define TRACE_SANITIZE() HB_STMT_START {} HB_STMT_END
131#endif
132
133#define SANITIZE_ARG_DEF \
134	hb_sanitize_context_t *context TRACE_SANITIZE_ARG_DEF
135#define SANITIZE_ARG \
136	context TRACE_SANITIZE_ARG
137#define SANITIZE_ARG_INIT \
138	&context TRACE_SANITIZE_ARG_INIT
139
140typedef struct _hb_sanitize_context_t hb_sanitize_context_t;
141struct _hb_sanitize_context_t
142{
143  const char *start, *end;
144  int edit_count;
145  hb_blob_t *blob;
146};
147
148static HB_GNUC_UNUSED void
149_hb_sanitize_init (hb_sanitize_context_t *context,
150		   hb_blob_t *blob)
151{
152  context->blob = blob;
153  context->start = hb_blob_lock (blob);
154  context->end = context->start + hb_blob_get_length (blob);
155  context->edit_count = 0;
156
157#if HB_DEBUG_SANITIZE
158  fprintf (stderr, "sanitize %p init [%p..%p] (%u bytes)\n",
159	   context->blob, context->start, context->end, context->end - context->start);
160#endif
161}
162
163static HB_GNUC_UNUSED void
164_hb_sanitize_fini (hb_sanitize_context_t *context,
165		   bool unlock)
166{
167#if HB_DEBUG_SANITIZE
168  fprintf (stderr, "sanitize %p fini [%p..%p] %u edit requests\n",
169	   context->blob, context->start, context->end, context->edit_count);
170#endif
171
172  if (unlock)
173    hb_blob_unlock (context->blob);
174}
175
176static HB_GNUC_UNUSED inline bool
177_hb_sanitize_check (SANITIZE_ARG_DEF,
178		    const char *base,
179		    unsigned int len)
180{
181  bool ret = context->start <= base &&
182	     base <= context->end &&
183	     (unsigned int) (context->end - base) >= len;
184
185#if HB_DEBUG_SANITIZE
186  if (sanitize_depth < HB_DEBUG_SANITIZE) \
187    fprintf (stderr, "SANITIZE(%p) %-*d-> check [%p..%p] (%d bytes) in [%p..%p] -> %s\n", \
188	     base,
189	     sanitize_depth, sanitize_depth,
190	     base, base+len, len,
191	     context->start, context->end,
192	     ret ? "pass" : "FAIL");
193#endif
194  return ret;
195}
196
197static HB_GNUC_UNUSED inline bool
198_hb_sanitize_array (SANITIZE_ARG_DEF,
199		    const char *base,
200		    unsigned int record_size,
201		    unsigned int len)
202{
203  bool overflows = len >= ((unsigned int) -1) / record_size;
204
205#if HB_DEBUG_SANITIZE
206  if (sanitize_depth < HB_DEBUG_SANITIZE) \
207    fprintf (stderr, "SANITIZE(%p) %-*d-> array [%p..%p] (%d*%d=%ld bytes) in [%p..%p] -> %s\n", \
208	     base,
209	     sanitize_depth, sanitize_depth,
210	     base, base + (record_size * len), record_size, len, (unsigned long) record_size * len,
211	     context->start, context->end,
212	     !overflows ? "does not overflow" : "OVERFLOWS FAIL");
213#endif
214  return HB_LIKELY (!overflows) && _hb_sanitize_check (SANITIZE_ARG, base, record_size * len);
215}
216
217static HB_GNUC_UNUSED inline bool
218_hb_sanitize_edit (SANITIZE_ARG_DEF,
219		   const char *base HB_GNUC_UNUSED,
220		   unsigned int len HB_GNUC_UNUSED)
221{
222  bool perm = hb_blob_try_writable_inplace (context->blob);
223  context->edit_count++;
224
225#if HB_DEBUG_SANITIZE
226  fprintf (stderr, "SANITIZE(%p) %-*d-> edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s\n", \
227	   base,
228	   sanitize_depth, sanitize_depth,
229	   context->edit_count,
230	   base, base+len, len,
231	   context->start, context->end,
232	   perm ? "granted" : "REJECTED");
233#endif
234  return perm;
235}
236
237#define SANITIZE(X) HB_LIKELY ((X).sanitize (SANITIZE_ARG))
238#define SANITIZE2(X,Y) (SANITIZE (X) && SANITIZE (Y))
239
240#define SANITIZE_THIS(X) HB_LIKELY ((X).sanitize (SANITIZE_ARG, CONST_CHARP(this)))
241#define SANITIZE_THIS2(X,Y) (SANITIZE_THIS (X) && SANITIZE_THIS (Y))
242#define SANITIZE_THIS3(X,Y,Z) (SANITIZE_THIS (X) && SANITIZE_THIS (Y) && SANITIZE_THIS(Z))
243
244#define SANITIZE_BASE(X,B) HB_LIKELY ((X).sanitize (SANITIZE_ARG, B))
245#define SANITIZE_BASE2(X,Y,B) (SANITIZE_BASE (X,B) && SANITIZE_BASE (Y,B))
246
247#define SANITIZE_SELF() SANITIZE_OBJ (*this)
248#define SANITIZE_OBJ(X) SANITIZE_MEM(&(X), sizeof (X))
249#define SANITIZE_GET_SIZE() SANITIZE_SELF() && SANITIZE_MEM (this, this->get_size ())
250
251#define SANITIZE_MEM(B,L) HB_LIKELY (_hb_sanitize_check (SANITIZE_ARG, CONST_CHARP(B), (L)))
252
253#define SANITIZE_ARRAY(A,S,L) HB_LIKELY (_hb_sanitize_array (SANITIZE_ARG, CONST_CHARP(A), S, L))
254
255#define NEUTER(Var, Val) \
256	(SANITIZE_OBJ (Var) && \
257	 _hb_sanitize_edit (SANITIZE_ARG, CONST_CHARP(&(Var)), sizeof (Var)) && \
258	 ((Var).set (Val), true))
259
260
261/* Template to sanitize an object. */
262template <typename Type>
263struct Sanitizer
264{
265  static hb_blob_t *sanitize (hb_blob_t *blob) {
266    hb_sanitize_context_t context;
267    bool sane;
268
269    /* TODO is_sane() stuff */
270
271  retry:
272#if HB_DEBUG_SANITIZE
273    fprintf (stderr, "Sanitizer %p start %s\n", blob, __PRETTY_FUNCTION__);
274#endif
275
276    _hb_sanitize_init (&context, blob);
277
278    Type *t = &CAST (Type, *DECONST_CHARP(context.start), 0);
279
280    sane = t->sanitize (SANITIZE_ARG_INIT);
281    if (sane) {
282      if (context.edit_count) {
283#if HB_DEBUG_SANITIZE
284	fprintf (stderr, "Sanitizer %p passed first round with %d edits; going a second round %s\n",
285		 blob, context.edit_count, __PRETTY_FUNCTION__);
286#endif
287        /* sanitize again to ensure no toe-stepping */
288        context.edit_count = 0;
289	sane = t->sanitize (SANITIZE_ARG_INIT);
290	if (context.edit_count) {
291#if HB_DEBUG_SANITIZE
292	  fprintf (stderr, "Sanitizer %p requested %d edits in second round; FAILLING %s\n",
293		   blob, context.edit_count, __PRETTY_FUNCTION__);
294#endif
295	  sane = false;
296	}
297      }
298      _hb_sanitize_fini (&context, true);
299    } else {
300      unsigned int edit_count = context.edit_count;
301      _hb_sanitize_fini (&context, true);
302      if (edit_count && !hb_blob_is_writable (blob) && hb_blob_try_writable (blob)) {
303        /* ok, we made it writable by relocating.  try again */
304#if HB_DEBUG_SANITIZE
305	fprintf (stderr, "Sanitizer %p retry %s\n", blob, __PRETTY_FUNCTION__);
306#endif
307        goto retry;
308      }
309    }
310
311#if HB_DEBUG_SANITIZE
312    fprintf (stderr, "Sanitizer %p %s %s\n", blob, sane ? "passed" : "FAILED", __PRETTY_FUNCTION__);
313#endif
314    if (sane)
315      return blob;
316    else {
317      hb_blob_destroy (blob);
318      return hb_blob_create_empty ();
319    }
320  }
321
322  static const Type& lock_instance (hb_blob_t *blob) {
323    return Type::get_for_data (hb_blob_lock (blob));
324  }
325};
326
327
328/*
329 *
330 * The OpenType Font File: Data Types
331 */
332
333
334/* "The following data types are used in the OpenType font file.
335 *  All OpenType fonts use Motorola-style byte ordering (Big Endian):" */
336
337/*
338 * Int types
339 */
340
341#define DEFINE_INT_TYPE1(NAME, TYPE, BIG_ENDIAN, BYTES) \
342  struct NAME \
343  { \
344    static inline unsigned int get_size () { return BYTES; } \
345    inline void set (TYPE i) { BIG_ENDIAN##_put (v, i); } \
346    inline operator TYPE(void) const { return BIG_ENDIAN##_get (v); } \
347    inline bool operator == (const NAME &o) const { return BIG_ENDIAN##_cmp (v, o.v); } \
348    inline bool sanitize (SANITIZE_ARG_DEF) { \
349      TRACE_SANITIZE (); \
350      return SANITIZE_SELF (); \
351    } \
352    private: unsigned char v[BYTES]; \
353  }; \
354  ASSERT_SIZE (NAME, BYTES)
355#define DEFINE_INT_TYPE0(NAME, type, b)	DEFINE_INT_TYPE1 (NAME, type##_t, hb_be_##type, b)
356#define DEFINE_INT_TYPE(NAME, u, w)	DEFINE_INT_TYPE0 (NAME, u##int##w, (w / 8))
357
358
359DEFINE_INT_TYPE (USHORT, u, 16);	/* 16-bit unsigned integer. */
360DEFINE_INT_TYPE (SHORT,	  , 16);	/* 16-bit signed integer. */
361DEFINE_INT_TYPE (ULONG,	 u, 32);	/* 32-bit unsigned integer. */
362DEFINE_INT_TYPE (LONG,	  , 32);	/* 32-bit signed integer. */
363
364
365/* Array of four uint8s (length = 32 bits) used to identify a script, language
366 * system, feature, or baseline */
367struct Tag : ULONG
368{
369  /* What the char* converters return is NOT nul-terminated.  Print using "%.4s" */
370  inline operator const char* (void) const { return CONST_CHARP(this); }
371  inline operator char* (void) { return CHARP(this); }
372
373  inline bool sanitize (SANITIZE_ARG_DEF) {
374    TRACE_SANITIZE ();
375    /* Note: Only accept ASCII-visible tags (mind DEL)
376     * This is one of the few places (only place?) that we check
377     * for data integrity, as opposed to just boundary checks.
378     */
379    return SANITIZE_SELF () && (((uint32_t) *this) & 0x80808080) == 0;
380  }
381};
382ASSERT_SIZE (Tag, 4);
383DEFINE_NULL_DATA (Tag, 4, "    ");
384
385/* Glyph index number, same as uint16 (length = 16 bits) */
386typedef USHORT GlyphID;
387
388/* Offset to a table, same as uint16 (length = 16 bits), Null offset = 0x0000 */
389typedef USHORT Offset;
390
391/* LongOffset to a table, same as uint32 (length = 32 bits), Null offset = 0x00000000 */
392typedef ULONG LongOffset;
393
394
395/* CheckSum */
396struct CheckSum : ULONG
397{
398  static uint32_t CalcTableChecksum (ULONG *Table, uint32_t Length)
399  {
400    uint32_t Sum = 0L;
401    ULONG *EndPtr = Table+((Length+3) & ~3) / ULONG::get_size ();
402
403    while (Table < EndPtr)
404      Sum += *Table++;
405    return Sum;
406  }
407};
408ASSERT_SIZE (CheckSum, 4);
409
410
411/*
412 * Version Numbers
413 */
414
415struct FixedVersion
416{
417  inline operator uint32_t (void) const { return (major << 16) + minor; }
418
419  inline bool sanitize (SANITIZE_ARG_DEF) {
420    TRACE_SANITIZE ();
421    return SANITIZE_SELF ();
422  }
423
424  USHORT major;
425  USHORT minor;
426};
427ASSERT_SIZE (FixedVersion, 4);
428
429
430
431/*
432 * Template subclasses of Offset and LongOffset that do the dereferencing.
433 * Use: (this+memberName)
434 */
435
436template <typename OffsetType, typename Type>
437struct GenericOffsetTo : OffsetType
438{
439  inline const Type& operator () (const void *base) const
440  {
441    unsigned int offset = *this;
442    if (HB_UNLIKELY (!offset)) return Null(Type);
443    return CONST_CAST(Type, *CONST_CHARP(base), offset);
444  }
445
446  inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
447    TRACE_SANITIZE ();
448    if (!SANITIZE_SELF ()) return false;
449    unsigned int offset = *this;
450    if (HB_UNLIKELY (!offset)) return true;
451    return SANITIZE (CAST(Type, *DECONST_CHARP(base), offset)) || NEUTER (DECONST_CAST(OffsetType,*this,0), 0);
452  }
453  inline bool sanitize (SANITIZE_ARG_DEF, const void *base, const void *base2) {
454    TRACE_SANITIZE ();
455    if (!SANITIZE_SELF ()) return false;
456    unsigned int offset = *this;
457    if (HB_UNLIKELY (!offset)) return true;
458    return SANITIZE_BASE (CAST(Type, *DECONST_CHARP(base), offset), base2) || NEUTER (DECONST_CAST(OffsetType,*this,0), 0);
459  }
460  inline bool sanitize (SANITIZE_ARG_DEF, const void *base, unsigned int user_data) {
461    TRACE_SANITIZE ();
462    if (!SANITIZE_SELF ()) return false;
463    unsigned int offset = *this;
464    if (HB_UNLIKELY (!offset)) return true;
465    return SANITIZE_BASE (CAST(Type, *DECONST_CHARP(base), offset), user_data) || NEUTER (DECONST_CAST(OffsetType,*this,0), 0);
466  }
467};
468template <typename Base, typename OffsetType, typename Type>
469inline const Type& operator + (const Base &base, GenericOffsetTo<OffsetType, Type> offset) { return offset (base); }
470
471template <typename Type>
472struct OffsetTo : GenericOffsetTo<Offset, Type> {};
473
474template <typename Type>
475struct LongOffsetTo : GenericOffsetTo<LongOffset, Type> {};
476
477
478/*
479 * Array Types
480 */
481
482template <typename LenType, typename Type>
483struct GenericArrayOf
484{
485  const Type *const_array(void) const { return CONST_ARRAY_AFTER (Type, len); }
486  Type *array(void) { return ARRAY_AFTER (Type, len); }
487
488  const Type *const_sub_array (unsigned int start_offset, unsigned int *pcount /* IN/OUT */) const
489  {
490    unsigned int count = len;
491    if (HB_UNLIKELY (start_offset > count))
492      count = 0;
493    else
494      count -= start_offset;
495    count = MIN (count, *pcount);
496    *pcount = count;
497    return const_array() + start_offset;
498  }
499
500  inline const Type& operator [] (unsigned int i) const
501  {
502    if (HB_UNLIKELY (i >= len)) return Null(Type);
503    return const_array()[i];
504  }
505  inline unsigned int get_size () const
506  { return len.get_size () + len * Type::get_size (); }
507
508  inline bool sanitize (SANITIZE_ARG_DEF) {
509    TRACE_SANITIZE ();
510    if (!SANITIZE_GET_SIZE()) return false;
511    /* Note: for structs that do not reference other structs,
512     * we do not need to call their sanitize() as we already did
513     * a bound check on the aggregate array size, hence the return.
514     */
515    return true;
516    /* We do keep this code though to make sure the structs pointed
517     * to do have a simple sanitize(), ie. they do not reference
518     * other structs. */
519    unsigned int count = len;
520    for (unsigned int i = 0; i < count; i++)
521      if (!SANITIZE (array()[i]))
522        return false;
523    return true;
524  }
525  inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
526    TRACE_SANITIZE ();
527    if (!SANITIZE_GET_SIZE()) return false;
528    unsigned int count = len;
529    for (unsigned int i = 0; i < count; i++)
530      if (!array()[i].sanitize (SANITIZE_ARG, base))
531        return false;
532    return true;
533  }
534  inline bool sanitize (SANITIZE_ARG_DEF, const void *base, const void *base2) {
535    TRACE_SANITIZE ();
536    if (!SANITIZE_GET_SIZE()) return false;
537    unsigned int count = len;
538    for (unsigned int i = 0; i < count; i++)
539      if (!array()[i].sanitize (SANITIZE_ARG, base, base2))
540        return false;
541    return true;
542  }
543  inline bool sanitize (SANITIZE_ARG_DEF, const void *base, unsigned int user_data) {
544    TRACE_SANITIZE ();
545    if (!SANITIZE_GET_SIZE()) return false;
546    unsigned int count = len;
547    for (unsigned int i = 0; i < count; i++)
548      if (!array()[i].sanitize (SANITIZE_ARG, base, user_data))
549        return false;
550    return true;
551  }
552
553  LenType len;
554/*Type array[VAR];*/
555};
556
557/* An array with a USHORT number of elements. */
558template <typename Type>
559struct ArrayOf : GenericArrayOf<USHORT, Type> {};
560
561/* An array with a ULONG number of elements. */
562template <typename Type>
563struct LongArrayOf : GenericArrayOf<ULONG, Type> {};
564
565/* Array of Offset's */
566template <typename Type>
567struct OffsetArrayOf : ArrayOf<OffsetTo<Type> > {};
568
569/* Array of LongOffset's */
570template <typename Type>
571struct LongOffsetArrayOf : ArrayOf<LongOffsetTo<Type> > {};
572
573/* LongArray of LongOffset's */
574template <typename Type>
575struct LongOffsetLongArrayOf : LongArrayOf<LongOffsetTo<Type> > {};
576
577/* Array of offsets relative to the beginning of the array itself. */
578template <typename Type>
579struct OffsetListOf : OffsetArrayOf<Type>
580{
581  inline const Type& operator [] (unsigned int i) const
582  {
583    if (HB_UNLIKELY (i >= this->len)) return Null(Type);
584    return this+this->const_array()[i];
585  }
586
587  inline bool sanitize (SANITIZE_ARG_DEF) {
588    TRACE_SANITIZE ();
589    return OffsetArrayOf<Type>::sanitize (SANITIZE_ARG, CONST_CHARP(this));
590  }
591  inline bool sanitize (SANITIZE_ARG_DEF, unsigned int user_data) {
592    TRACE_SANITIZE ();
593    return OffsetArrayOf<Type>::sanitize (SANITIZE_ARG, CONST_CHARP(this), user_data);
594  }
595};
596
597
598/* An array with a USHORT number of elements,
599 * starting at second element. */
600template <typename Type>
601struct HeadlessArrayOf
602{
603  const Type *const_array(void) const { return CONST_ARRAY_AFTER (Type, len); }
604  Type *array(void) { return ARRAY_AFTER (Type, len); }
605
606  inline const Type& operator [] (unsigned int i) const
607  {
608    if (HB_UNLIKELY (i >= len || !i)) return Null(Type);
609    return const_array()[i-1];
610  }
611  inline unsigned int get_size () const
612  { return len.get_size () + (len ? len - 1 : 0) * Type::get_size (); }
613
614  inline bool sanitize (SANITIZE_ARG_DEF) {
615    TRACE_SANITIZE ();
616    if (!SANITIZE_GET_SIZE()) return false;
617    /* Note: for structs that do not reference other structs,
618     * we do not need to call their sanitize() as we already did
619     * a bound check on the aggregate array size, hence the return.
620     */
621    return true;
622    /* We do keep this code though to make sure the structs pointed
623     * to do have a simple sanitize(), ie. they do not reference
624     * other structs. */
625    unsigned int count = len ? len - 1 : 0;
626    Type *a = array();
627    for (unsigned int i = 0; i < count; i++)
628      if (!SANITIZE (a[i]))
629        return false;
630    return true;
631  }
632
633  USHORT len;
634/*Type array[VAR];*/
635};
636
637
638#endif /* HB_OPEN_TYPE_PRIVATE_HH */
639