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