hb-open-type-private.hh revision 20b035dad41247076815a2bbb0346d63058b322f
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_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#define NO_CONTEXT		((unsigned int) 0x110000)
37#define NOT_COVERED		((unsigned int) 0x110000)
38#define MAX_NESTING_LEVEL	8
39
40
41/*
42 * Casts
43 */
44
45#define CONST_CHARP(X)		(reinterpret_cast<const char *>(X))
46#define DECONST_CHARP(X)	((char *)reinterpret_cast<const char *>(X))
47#define CHARP(X)		(reinterpret_cast<char *>(X))
48
49#define CONST_CAST(T,X,Ofs)	(*(reinterpret_cast<const T *>(CONST_CHARP(&(X)) + Ofs)))
50#define DECONST_CAST(T,X,Ofs)	(*(reinterpret_cast<T *>((char *)CONST_CHARP(&(X)) + Ofs)))
51#define CAST(T,X,Ofs) 		(*(reinterpret_cast<T *>(CHARP(&(X)) + Ofs)))
52
53#define CONST_NEXT(T,X)		(*(reinterpret_cast<const T *>(CONST_CHARP(&(X)) + (X).get_size ())))
54#define NEXT(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 char NullPool[16] = "";
65
66/* Generic template for nul-content sizeof-sized Null objects. */
67template <typename Type>
68struct Null
69{
70  ASSERT_STATIC (sizeof (Type) <= sizeof (NullPool));
71  static inline const Type &get () { return *(const Type*)NullPool; }
72};
73
74/* Specializaiton for arbitrary-content arbitrary-sized Null objects. */
75#define DEFINE_NULL_DATA(Type, size, data) \
76static const char _Null##Type[size] = data; \
77template <> \
78struct Null <Type> \
79{ \
80  static inline const Type &get () { return *(const Type*)_Null##Type; } \
81}
82
83/* Accessor macro. */
84#define Null(Type) (Null<Type>::get())
85
86
87#define ASSERT_SIZE_DATA(Type, size, data) \
88  ASSERT_SIZE (Type, size); \
89  DEFINE_NULL_DATA (Type, size, data)
90
91/* get_for_data() is a static class method returning a reference to an
92 * instance of Type located at the input data location.  It's just a
93 * fancy, NULL-safe, cast! */
94#define STATIC_DEFINE_GET_FOR_DATA(Type) \
95  static inline const Type& get_for_data (const char *data) \
96  { \
97    if (HB_UNLIKELY (data == NULL)) return Null(Type); \
98    return *(const Type*)data; \
99  }
100/* Like get_for_data(), but checks major version first. */
101#define STATIC_DEFINE_GET_FOR_DATA_CHECK_MAJOR_VERSION(Type, MajorMin, MajorMax) \
102  static inline const Type& get_for_data (const char *data) \
103  { \
104    if (HB_UNLIKELY (data == NULL)) return Null(Type); \
105    const Type& t = *(const Type*)data; \
106    if (HB_UNLIKELY (t.version.major < MajorMin || t.version.major > MajorMax)) return Null(Type); \
107    return t; \
108  }
109
110
111/*
112 * Sanitize
113 */
114
115#if HB_DEBUG
116#define SANITIZE_DEBUG_ARG_DEF	, unsigned int sanitize_depth
117#define SANITIZE_DEBUG_ARG	, sanitize_depth + 1
118#define SANITIZE_DEBUG_ARG_INIT	, 1
119#define SANITIZE_DEBUG() \
120	HB_STMT_START { \
121	    if (sanitize_depth < HB_DEBUG) \
122		fprintf (stderr, "SANITIZE(%p) %-*d-> %s\n", \
123			 (CONST_CHARP (this) == NullPool) ? 0 : this, \
124			 sanitize_depth, sanitize_depth, \
125			 __PRETTY_FUNCTION__); \
126	} HB_STMT_END
127#else
128#define SANITIZE_DEBUG_ARG_DEF
129#define SANITIZE_DEBUG_ARG
130#define SANITIZE_DEBUG_ARG_INIT
131#define SANITIZE_DEBUG() HB_STMT_START {} HB_STMT_END
132#endif
133
134typedef struct _hb_sanitize_context_t hb_sanitize_context_t;
135struct _hb_sanitize_context_t
136{
137  const char *start, *end;
138  int edit_count;
139  hb_blob_t *blob;
140};
141
142static HB_GNUC_UNUSED void
143_hb_sanitize_init (hb_sanitize_context_t *context,
144		   hb_blob_t *blob)
145{
146  context->blob = blob;
147  context->start = hb_blob_lock (blob);
148  context->end = context->start + hb_blob_get_length (blob);
149  context->edit_count = 0;
150
151#if HB_DEBUG
152  fprintf (stderr, "sanitize %p init [%p..%p] (%u bytes)\n",
153	   context->blob, context->start, context->end, context->end - context->start);
154#endif
155}
156
157static HB_GNUC_UNUSED void
158_hb_sanitize_fini (hb_sanitize_context_t *context,
159		   bool unlock)
160{
161#if HB_DEBUG
162  fprintf (stderr, "sanitize %p fini [%p..%p] %u edit requests\n",
163	   context->blob, context->start, context->end, context->edit_count);
164#endif
165
166  if (unlock)
167    hb_blob_unlock (context->blob);
168}
169
170static HB_GNUC_UNUSED inline bool
171_hb_sanitize_edit (hb_sanitize_context_t *context,
172		   const char *base HB_GNUC_UNUSED,
173		   unsigned int len HB_GNUC_UNUSED)
174{
175  bool perm = hb_blob_try_writeable_inplace (context->blob);
176  context->edit_count++;
177
178#if HB_DEBUG
179  fprintf (stderr, "sanitize %p edit %u requested for [%p..%p] (%d bytes) in [%p..%p] -> %s\n",
180	   context->blob,
181	  context->edit_count,
182	  base, base+len, len,
183	  context->start, context->end,
184	  perm ? "granted" : "rejected");
185#endif
186  return perm;
187}
188
189#define SANITIZE_ARG_DEF \
190	hb_sanitize_context_t *context SANITIZE_DEBUG_ARG_DEF
191#define SANITIZE_ARG \
192	context SANITIZE_DEBUG_ARG
193#define SANITIZE_ARG_INIT \
194	&context SANITIZE_DEBUG_ARG_INIT
195
196#define SANITIZE(X) HB_LIKELY ((X).sanitize (SANITIZE_ARG))
197#define SANITIZE2(X,Y) (SANITIZE (X) && SANITIZE (Y))
198
199#define SANITIZE_THIS(X) HB_LIKELY ((X).sanitize (SANITIZE_ARG, CONST_CHARP(this)))
200#define SANITIZE_THIS2(X,Y) (SANITIZE_THIS (X) && SANITIZE_THIS (Y))
201#define SANITIZE_THIS3(X,Y,Z) (SANITIZE_THIS (X) && SANITIZE_THIS (Y) && SANITIZE_THIS(Z))
202
203#define SANITIZE_BASE(X,B) HB_LIKELY ((X).sanitize (SANITIZE_ARG, B))
204#define SANITIZE_BASE2(X,Y,B) (SANITIZE_BASE (X,B) && SANITIZE_BASE (Y,B))
205
206#define SANITIZE_SELF() SANITIZE_OBJ (*this)
207#define SANITIZE_OBJ(X) SANITIZE_MEM(&(X), sizeof (X))
208#define SANITIZE_GET_SIZE() SANITIZE_SELF() && SANITIZE_MEM (this, this->get_size ())
209
210/* TODO Optimize this if L is fixed (gcc magic) */
211#define SANITIZE_MEM(B,L) \
212	HB_LIKELY (context->start <= CONST_CHARP(B) && \
213		   CONST_CHARP(B) < context->end && \
214		   context->end - CONST_CHARP(B) >= (L))
215
216#define NEUTER(Var, Val) \
217	(SANITIZE_OBJ (Var) && \
218	 _hb_sanitize_edit (context, CONST_CHARP(&(Var)), sizeof (Var)) && \
219	 ((Var) = (Val), true))
220
221
222/* Template to sanitize an object. */
223template <typename Type>
224struct Sanitizer
225{
226  static hb_blob_t *sanitize (hb_blob_t *blob) {
227    hb_sanitize_context_t context;
228    bool sane;
229
230    /* TODO is_sane() stuff */
231
232  retry:
233#if HB_DEBUG
234    fprintf (stderr, "Sanitizer %p start %s\n", blob, __PRETTY_FUNCTION__);
235#endif
236
237    _hb_sanitize_init (&context, blob);
238
239    Type *t = &CAST (Type, *DECONST_CHARP(context.start), 0);
240
241    sane = t->sanitize (SANITIZE_ARG_INIT);
242    if (sane) {
243      if (context.edit_count) {
244#if HB_DEBUG
245	fprintf (stderr, "Sanitizer %p passed first round with %d edits; going a second round %s\n",
246		 blob, context.edit_count, __PRETTY_FUNCTION__);
247#endif
248        /* sanitize again to ensure not toe-stepping */
249        context.edit_count = 0;
250	sane = t->sanitize (SANITIZE_ARG_INIT);
251	if (context.edit_count) {
252#if HB_DEBUG
253	  fprintf (stderr, "Sanitizer %p requested %d edits in second round; failing %s\n",
254		   blob, context.edit_count, __PRETTY_FUNCTION__);
255#endif
256	  sane = false;
257	}
258      }
259      _hb_sanitize_fini (&context, true);
260    } else {
261      unsigned int edit_count = context.edit_count;
262      _hb_sanitize_fini (&context, true);
263      if (edit_count && !hb_blob_is_writeable (blob) && hb_blob_try_writeable (blob)) {
264        /* ok, we made it writeable by relocating.  try again */
265#if HB_DEBUG
266	fprintf (stderr, "Sanitizer %p retry %s\n", blob, __PRETTY_FUNCTION__);
267#endif
268        goto retry;
269      }
270    }
271
272#if HB_DEBUG
273    fprintf (stderr, "Sanitizer %p %s %s\n", blob, sane ? "passed" : "failed", __PRETTY_FUNCTION__);
274#endif
275    if (sane)
276      return blob;
277    else {
278      hb_blob_destroy (blob);
279      return hb_blob_create_empty ();
280    }
281  }
282
283  static const Type& lock_instance (hb_blob_t *blob) {
284    return Type::get_for_data (hb_blob_lock (blob));
285  }
286};
287
288
289/*
290 *
291 * The OpenType Font File: Data Types
292 */
293
294
295/* "The following data types are used in the OpenType font file.
296 *  All OpenType fonts use Motorola-style byte ordering (Big Endian):" */
297
298/*
299 * Int types
300 */
301
302/* TODO On machines that allow unaligned access, use this version. */
303#define _DEFINE_INT_TYPE1_UNALIGNED(NAME, TYPE, BIG_ENDIAN, BYTES) \
304  struct NAME \
305  { \
306    inline NAME& operator = (TYPE i) { (TYPE&) v = BIG_ENDIAN (i); return *this; } \
307    inline operator TYPE(void) const { return BIG_ENDIAN ((TYPE&) v); } \
308    inline bool operator== (NAME o) const { return (TYPE&) v == (TYPE&) o.v; } \
309    inline bool sanitize (SANITIZE_ARG_DEF) { \
310      SANITIZE_DEBUG (); \
311      return SANITIZE_SELF (); \
312    } \
313    private: unsigned char v[BYTES]; \
314  }; \
315  ASSERT_SIZE (NAME, BYTES)
316
317#define DEFINE_INT_TYPE1(NAME, TYPE, BIG_ENDIAN, BYTES) \
318  struct NAME \
319  { \
320    inline NAME& operator = (TYPE i) { BIG_ENDIAN##_put_unaligned(v, i); return *this; } \
321    inline operator TYPE(void) const { return BIG_ENDIAN##_get_unaligned (v); } \
322    inline bool operator== (NAME o) const { return BIG_ENDIAN##_cmp_unaligned (v, o.v); } \
323    inline bool sanitize (SANITIZE_ARG_DEF) { \
324      SANITIZE_DEBUG (); \
325      return SANITIZE_SELF (); \
326    } \
327    private: unsigned char v[BYTES]; \
328  }; \
329  ASSERT_SIZE (NAME, BYTES)
330#define DEFINE_INT_TYPE0(NAME, type, b)	DEFINE_INT_TYPE1 (NAME, type##_t, hb_be_##type, b)
331#define DEFINE_INT_TYPE(NAME, u, w)	DEFINE_INT_TYPE0 (NAME, u##int##w, (w / 8))
332
333
334DEFINE_INT_TYPE (USHORT,  u, 16);	/* 16-bit unsigned integer. */
335DEFINE_INT_TYPE (SHORT,	  , 16);	/* 16-bit signed integer. */
336DEFINE_INT_TYPE (ULONG,	 u, 32);	/* 32-bit unsigned integer. */
337DEFINE_INT_TYPE (LONG,	  , 32);	/* 32-bit signed integer. */
338
339
340/* Array of four uint8s (length = 32 bits) used to identify a script, language
341 * system, feature, or baseline */
342struct Tag : ULONG
343{
344  inline Tag (const Tag &o) { *(ULONG*)this = (ULONG&) o; }
345  inline Tag (uint32_t i) { *(ULONG*)this = i; }
346  inline Tag (const char *c) { *(ULONG*)this = *(ULONG*)c; }
347  inline bool operator== (const char *c) const { return *(ULONG*)this == *(ULONG*)c; }
348  /* What the char* converters return is NOT nul-terminated.  Print using "%.4s" */
349  inline operator const char* (void) const { return CONST_CHARP(this); }
350  inline operator char* (void) { return CHARP(this); }
351
352  inline bool sanitize (SANITIZE_ARG_DEF) {
353    SANITIZE_DEBUG ();
354    /* Note: Only accept ASCII-visible tags (mind DEL)
355     * This is one of the few times (only time?) we check
356     * for data integrity, as opposed o just boundary checks
357     */
358    return SANITIZE_SELF () && (((uint32_t) *this) & 0x80808080) == 0;
359  }
360};
361ASSERT_SIZE (Tag, 4);
362#define _NULL_TAG_INIT  {' ', ' ', ' ', ' '}
363DEFINE_NULL_DATA (Tag, 4, _NULL_TAG_INIT);
364#undef _NULL_TAG_INIT
365
366/* Glyph index number, same as uint16 (length = 16 bits) */
367typedef USHORT GlyphID;
368
369/* Offset to a table, same as uint16 (length = 16 bits), Null offset = 0x0000 */
370typedef USHORT Offset;
371
372/* LongOffset to a table, same as uint32 (length = 32 bits), Null offset = 0x00000000 */
373typedef ULONG LongOffset;
374
375
376/* CheckSum */
377struct CheckSum : ULONG
378{
379  static uint32_t CalcTableChecksum (ULONG *Table, uint32_t Length)
380  {
381    uint32_t Sum = 0L;
382    ULONG *EndPtr = Table+((Length+3) & ~3) / sizeof(ULONG);
383
384    while (Table < EndPtr)
385      Sum += *Table++;
386    return Sum;
387  }
388};
389ASSERT_SIZE (CheckSum, 4);
390
391
392/*
393 * Version Numbers
394 */
395
396struct FixedVersion
397{
398  inline operator uint32_t (void) const { return (major << 16) + minor; }
399
400  inline bool sanitize (SANITIZE_ARG_DEF) {
401    SANITIZE_DEBUG ();
402    return SANITIZE_SELF ();
403  }
404
405  USHORT major;
406  USHORT minor;
407};
408ASSERT_SIZE (FixedVersion, 4);
409
410
411
412/*
413 * Template subclasses of Offset and LongOffset that do the dereferencing.
414 * Use: (this+memberName)
415 */
416
417template <typename OffsetType, typename Type>
418struct GenericOffsetTo : OffsetType
419{
420  inline const Type& operator() (const void *base) const
421  {
422    unsigned int offset = *this;
423    if (HB_UNLIKELY (!offset)) return Null(Type);
424    return CONST_CAST(Type, *CONST_CHARP(base), offset);
425  }
426
427  inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
428    SANITIZE_DEBUG ();
429    if (!SANITIZE_OBJ (*this)) return false;
430    unsigned int offset = *this;
431    if (HB_UNLIKELY (!offset)) return true;
432    return SANITIZE (CAST(Type, *DECONST_CHARP(base), offset)) || NEUTER (DECONST_CAST(OffsetType,*this,0), 0);
433  }
434  inline bool sanitize (SANITIZE_ARG_DEF, const void *base, const void *base2) {
435    SANITIZE_DEBUG ();
436    if (!SANITIZE_OBJ (*this)) return false;
437    unsigned int offset = *this;
438    if (HB_UNLIKELY (!offset)) return true;
439    return SANITIZE_BASE (CAST(Type, *DECONST_CHARP(base), offset), base2) || NEUTER (DECONST_CAST(OffsetType,*this,0), 0);
440  }
441  inline bool sanitize (SANITIZE_ARG_DEF, const void *base, unsigned int user_data) {
442    SANITIZE_DEBUG ();
443    if (!SANITIZE_OBJ (*this)) return false;
444    unsigned int offset = *this;
445    if (HB_UNLIKELY (!offset)) return true;
446    return SANITIZE_BASE (CAST(Type, *DECONST_CHARP(base), offset), user_data) || NEUTER (DECONST_CAST(OffsetType,*this,0), 0);
447  }
448};
449template <typename Base, typename OffsetType, typename Type>
450inline const Type& operator + (const Base &base, GenericOffsetTo<OffsetType, Type> offset) { return offset (base); }
451
452template <typename Type>
453struct OffsetTo : GenericOffsetTo<Offset, Type> {};
454
455template <typename Type>
456struct LongOffsetTo : GenericOffsetTo<LongOffset, Type> {};
457
458
459/*
460 * Array Types
461 */
462
463template <typename LenType, typename Type>
464struct GenericArrayOf
465{
466  inline const Type& operator [] (unsigned int i) const
467  {
468    if (HB_UNLIKELY (i >= len)) return Null(Type);
469    return array[i];
470  }
471  inline unsigned int get_size () const
472  { return sizeof (len) + len * sizeof (array[0]); }
473
474  inline bool sanitize (SANITIZE_ARG_DEF) {
475    SANITIZE_DEBUG ();
476    if (!SANITIZE_GET_SIZE()) return false;
477    /* Note; for non-recursive types, this is not much needed
478    unsigned int count = len;
479    for (unsigned int i = 0; i < count; i++)
480      if (!SANITIZE (array[i]))
481        return false;
482    */
483    return true;
484  }
485  inline bool sanitize (SANITIZE_ARG_DEF, const void *base) {
486    SANITIZE_DEBUG ();
487    if (!SANITIZE_GET_SIZE()) return false;
488    unsigned int count = len;
489    for (unsigned int i = 0; i < count; i++)
490      if (!array[i].sanitize (SANITIZE_ARG, base))
491        return false;
492    return true;
493  }
494  inline bool sanitize (SANITIZE_ARG_DEF, const void *base, const void *base2) {
495    SANITIZE_DEBUG ();
496    if (!SANITIZE_GET_SIZE()) return false;
497    unsigned int count = len;
498    for (unsigned int i = 0; i < count; i++)
499      if (!array[i].sanitize (SANITIZE_ARG, base, base2))
500        return false;
501    return true;
502  }
503  inline bool sanitize (SANITIZE_ARG_DEF, const void *base, unsigned int user_data) {
504    SANITIZE_DEBUG ();
505    if (!SANITIZE_GET_SIZE()) return false;
506    unsigned int count = len;
507    for (unsigned int i = 0; i < count; i++)
508      if (!array[i].sanitize (SANITIZE_ARG, base, user_data))
509        return false;
510    return true;
511  }
512
513  LenType len;
514  Type array[];
515};
516
517/* An array with a USHORT number of elements. */
518template <typename Type>
519struct ArrayOf : GenericArrayOf<USHORT, Type> {};
520
521/* An array with a ULONG number of elements. */
522template <typename Type>
523struct LongArrayOf : GenericArrayOf<ULONG, Type> {};
524
525/* Array of Offset's */
526template <typename Type>
527struct OffsetArrayOf : ArrayOf<OffsetTo<Type> > {};
528
529/* Array of LongOffset's */
530template <typename Type>
531struct LongOffsetArrayOf : ArrayOf<LongOffsetTo<Type> > {};
532
533/* LongArray of LongOffset's */
534template <typename Type>
535struct LongOffsetLongArrayOf : LongArrayOf<LongOffsetTo<Type> > {};
536
537/* An array with a USHORT number of elements,
538 * starting at second element. */
539template <typename Type>
540struct HeadlessArrayOf
541{
542  inline const Type& operator [] (unsigned int i) const
543  {
544    if (HB_UNLIKELY (i >= len || !i)) return Null(Type);
545    return array[i-1];
546  }
547  inline unsigned int get_size () const
548  { return sizeof (len) + (len ? len - 1 : 0) * sizeof (array[0]); }
549
550  inline bool sanitize (SANITIZE_ARG_DEF) {
551    SANITIZE_DEBUG ();
552    if (!SANITIZE_GET_SIZE()) return false;
553    /* Note; for non-recursive types, this is not much needed
554    unsigned int count = len ? len - 1 : 0;
555    for (unsigned int i = 0; i < count; i++)
556      if (!SANITIZE (array[i]))
557        return false;
558    */
559    return true;
560  }
561
562  USHORT len;
563  Type array[];
564};
565
566
567#endif /* HB_OPEN_TYPES_PRIVATE_HH */
568