diff --git a/src/Makefile.sources b/src/Makefile.sources index 9fa926d40..fb4e4a2c4 100644 --- a/src/Makefile.sources +++ b/src/Makefile.sources @@ -16,6 +16,7 @@ HB_BASE_sources = \ hb-font.cc \ hb-map-private.hh \ hb-map.cc \ + hb-machinery-private.hh \ hb-mutex-private.hh \ hb-object-private.hh \ hb-open-file-private.hh \ diff --git a/src/hb-machinery-private.hh b/src/hb-machinery-private.hh new file mode 100644 index 000000000..5fafadbec --- /dev/null +++ b/src/hb-machinery-private.hh @@ -0,0 +1,702 @@ +/* + * Copyright © 2007,2008,2009,2010 Red Hat, Inc. + * Copyright © 2012,2018 Google, Inc. + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + * + * Red Hat Author(s): Behdad Esfahbod + * Google Author(s): Behdad Esfahbod + */ + +#ifndef HB_MACHINERY_PRIVATE_HH +#define HB_MACHINERY_PRIVATE_HH + +#include "hb-private.hh" + + +namespace OT { + + +/* + * Casts + */ + +/* Cast to struct T, reference to reference */ +template +static inline const Type& CastR(const TObject &X) +{ return reinterpret_cast (X); } +template +static inline Type& CastR(TObject &X) +{ return reinterpret_cast (X); } + +/* Cast to struct T, pointer to pointer */ +template +static inline const Type* CastP(const TObject *X) +{ return reinterpret_cast (X); } +template +static inline Type* CastP(TObject *X) +{ return reinterpret_cast (X); } + +/* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory + * location pointed to by P plus Ofs bytes. */ +template +static inline const Type& StructAtOffset(const void *P, unsigned int offset) +{ return * reinterpret_cast ((const char *) P + offset); } +template +static inline Type& StructAtOffset(void *P, unsigned int offset) +{ return * reinterpret_cast ((char *) P + offset); } + +/* StructAfter(X) returns the struct T& that is placed after X. + * Works with X of variable size also. X must implement get_size() */ +template +static inline const Type& StructAfter(const TObject &X) +{ return StructAtOffset(&X, X.get_size()); } +template +static inline Type& StructAfter(TObject &X) +{ return StructAtOffset(&X, X.get_size()); } + + +/* + * Size checking + */ + +/* Check _assertion in a method environment */ +#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ + inline void _instance_assertion_on_line_##_line (void) const \ + { \ + static_assert ((_assertion), ""); \ + ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \ + } +# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) +# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) + +/* Check that _code compiles in a method environment */ +#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ + inline void _compiles_assertion_on_line_##_line (void) const \ + { _code; } +# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) +# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) + + +#define DEFINE_SIZE_STATIC(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \ + static const unsigned int static_size = (size); \ + static const unsigned int min_size = (size); \ + inline unsigned int get_size (void) const { return (size); } + +#define DEFINE_SIZE_UNION(size, _member) \ + DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \ + static const unsigned int min_size = (size) + +#define DEFINE_SIZE_MIN(size) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \ + static const unsigned int min_size = (size) + +#define DEFINE_SIZE_ARRAY(size, array) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \ + DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \ + static const unsigned int min_size = (size) + +#define DEFINE_SIZE_ARRAY2(size, array1, array2) \ + DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \ + DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \ + static const unsigned int min_size = (size) + + +/* + * Dispatch + */ + +template +struct hb_dispatch_context_t +{ + static const unsigned int max_debug_depth = MaxDebugDepth; + typedef Return return_t; + template + inline bool may_dispatch (const T *obj, const F *format) { return true; } + static return_t no_dispatch_return_value (void) { return Context::default_return_value (); } +}; + + +/* + * Sanitize + */ + +/* This limits sanitizing time on really broken fonts. */ +#ifndef HB_SANITIZE_MAX_EDITS +#define HB_SANITIZE_MAX_EDITS 32 +#endif +#ifndef HB_SANITIZE_MAX_OPS_FACTOR +#define HB_SANITIZE_MAX_OPS_FACTOR 8 +#endif +#ifndef HB_SANITIZE_MAX_OPS_MIN +#define HB_SANITIZE_MAX_OPS_MIN 16384 +#endif + +struct hb_sanitize_context_t : + hb_dispatch_context_t +{ + inline hb_sanitize_context_t (void) : + debug_depth (0), + start (nullptr), end (nullptr), + writable (false), edit_count (0), max_ops (0), + blob (nullptr), + num_glyphs (65536), + num_glyphs_set (false) {} + + inline const char *get_name (void) { return "SANITIZE"; } + template + inline bool may_dispatch (const T *obj, const F *format) + { return format->sanitize (this); } + template + inline return_t dispatch (const T &obj) { return obj.sanitize (this); } + static return_t default_return_value (void) { return true; } + static return_t no_dispatch_return_value (void) { return false; } + bool stop_sublookup_iteration (const return_t r) const { return !r; } + + inline void init (hb_blob_t *b) + { + this->blob = hb_blob_reference (b); + this->writable = false; + } + + inline void set_num_glyphs (unsigned int num_glyphs_) + { + num_glyphs = num_glyphs_; + num_glyphs_set = true; + } + inline unsigned int get_num_glyphs (void) { return num_glyphs; } + + inline void start_processing (void) + { + this->start = hb_blob_get_data (this->blob, nullptr); + this->end = this->start + this->blob->length; + assert (this->start <= this->end); /* Must not overflow. */ + this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, + (unsigned) HB_SANITIZE_MAX_OPS_MIN); + this->edit_count = 0; + this->debug_depth = 0; + + DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + } + + inline void end_processing (void) + { + DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, + "end [%p..%p] %u edit requests", + this->start, this->end, this->edit_count); + + hb_blob_destroy (this->blob); + this->blob = nullptr; + this->start = this->end = nullptr; + } + + inline bool check_range (const void *base, unsigned int len) const + { + const char *p = (const char *) base; + bool ok = this->max_ops-- > 0 && + this->start <= p && + p <= this->end && + (unsigned int) (this->end - p) >= len; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", + p, p + len, len, + this->start, this->end, + ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const + { + const char *p = (const char *) base; + bool overflows = hb_unsigned_mul_overflows (len, record_size); + unsigned int array_size = record_size * len; + bool ok = !overflows && this->check_range (base, array_size); + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s", + p, p + (record_size * len), record_size, len, (unsigned int) array_size, + this->start, this->end, + overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE"); + + return likely (ok); + } + + template + inline bool check_struct (const Type *obj) const + { + return likely (this->check_range (obj, obj->min_size)); + } + + inline bool may_edit (const void *base, unsigned int len) + { + if (this->edit_count >= HB_SANITIZE_MAX_EDITS) + return false; + + const char *p = (const char *) base; + this->edit_count++; + + DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, + "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", + this->edit_count, + p, p + len, len, + this->start, this->end, + this->writable ? "GRANTED" : "DENIED"); + + return this->writable; + } + + template + inline bool try_set (const Type *obj, const ValueType &v) { + if (this->may_edit (obj, obj->static_size)) { + const_cast (obj)->set (v); + return true; + } + return false; + } + + template + inline hb_blob_t *sanitize_blob (hb_blob_t *blob) + { + bool sane; + + init (blob); + + retry: + DEBUG_MSG_FUNC (SANITIZE, start, "start"); + + start_processing (); + + if (unlikely (!start)) + { + end_processing (); + return blob; + } + + Type *t = CastP (const_cast (start)); + + sane = t->sanitize (this); + if (sane) + { + if (edit_count) + { + DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count); + + /* sanitize again to ensure no toe-stepping */ + edit_count = 0; + sane = t->sanitize (this); + if (edit_count) { + DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count); + sane = false; + } + } + } + else + { + if (edit_count && !writable) { + start = hb_blob_get_data_writable (blob, nullptr); + end = start + blob->length; + + if (start) + { + writable = true; + /* ok, we made it writable by relocating. try again */ + DEBUG_MSG_FUNC (SANITIZE, start, "retry"); + goto retry; + } + } + } + + end_processing (); + + DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED"); + if (sane) + { + blob->lock (); + return blob; + } + else + { + hb_blob_destroy (blob); + return hb_blob_get_empty (); + } + } + + template + inline hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag) + { + if (!num_glyphs_set) + set_num_glyphs (face->get_num_glyphs ()); + return sanitize_blob (face->reference_table (tableTag)); + } + + mutable unsigned int debug_depth; + const char *start, *end; + private: + bool writable; + unsigned int edit_count; + mutable int max_ops; + hb_blob_t *blob; + unsigned int num_glyphs; + bool num_glyphs_set; +}; + + +/* + * Serialize + */ + +struct hb_serialize_context_t +{ + inline hb_serialize_context_t (void *start_, unsigned int size) + { + this->start = (char *) start_; + this->end = this->start + size; + + this->ran_out_of_room = false; + this->head = this->start; + this->debug_depth = 0; + } + + template + inline Type *start_serialize (void) + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, + "start [%p..%p] (%lu bytes)", + this->start, this->end, + (unsigned long) (this->end - this->start)); + + return start_embed (); + } + + inline void end_serialize (void) + { + DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, + "end [%p..%p] serialized %d bytes; %s", + this->start, this->end, + (int) (this->head - this->start), + this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room"); + + } + + template + inline Type *copy (void) + { + assert (!this->ran_out_of_room); + unsigned int len = this->head - this->start; + void *p = malloc (len); + if (p) + memcpy (p, this->start, len); + return reinterpret_cast (p); + } + + template + inline Type *allocate_size (unsigned int size) + { + if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) { + this->ran_out_of_room = true; + return nullptr; + } + memset (this->head, 0, size); + char *ret = this->head; + this->head += size; + return reinterpret_cast (ret); + } + + template + inline Type *allocate_min (void) + { + return this->allocate_size (Type::min_size); + } + + template + inline Type *start_embed (void) + { + Type *ret = reinterpret_cast (this->head); + return ret; + } + + template + inline Type *embed (const Type &obj) + { + unsigned int size = obj.get_size (); + Type *ret = this->allocate_size (size); + if (unlikely (!ret)) return nullptr; + memcpy (ret, obj, size); + return ret; + } + + template + inline Type *extend_min (Type &obj) + { + unsigned int size = obj.min_size; + assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); + if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; + return reinterpret_cast (&obj); + } + + template + inline Type *extend (Type &obj) + { + unsigned int size = obj.get_size (); + assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); + if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; + return reinterpret_cast (&obj); + } + + inline void truncate (void *new_head) + { + assert (this->start < new_head && new_head <= this->head); + this->head = (char *) new_head; + } + + unsigned int debug_depth; + char *start, *end, *head; + bool ran_out_of_room; +}; + + +/* + * Supplier + */ + +template +struct Supplier +{ + inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type)) + { + head = array; + len = len_; + stride = stride_; + } + inline const Type operator [] (unsigned int i) const + { + if (unlikely (i >= len)) return Type (); + return * (const Type *) (const void *) ((const char *) head + stride * i); + } + + inline Supplier & operator += (unsigned int count) + { + if (unlikely (count > len)) + count = len; + len -= count; + head = (const Type *) (const void *) ((const char *) head + stride * count); + return *this; + } + + private: + inline Supplier (const Supplier &); /* Disallow copy */ + inline Supplier& operator= (const Supplier &); /* Disallow copy */ + + unsigned int len; + unsigned int stride; + const Type *head; +}; + + +/* + * Big-endian integers. + */ + +template struct BEInt; + +template +struct BEInt +{ + public: + inline void set (Type V) + { + v = V; + } + inline operator Type (void) const + { + return v; + } + private: uint8_t v; +}; +template +struct BEInt +{ + public: + inline void set (Type V) + { + v[0] = (V >> 8) & 0xFF; + v[1] = (V ) & 0xFF; + } + inline operator Type (void) const + { + return (v[0] << 8) + + (v[1] ); + } + private: uint8_t v[2]; +}; +template +struct BEInt +{ + public: + inline void set (Type V) + { + v[0] = (V >> 16) & 0xFF; + v[1] = (V >> 8) & 0xFF; + v[2] = (V ) & 0xFF; + } + inline operator Type (void) const + { + return (v[0] << 16) + + (v[1] << 8) + + (v[2] ); + } + private: uint8_t v[3]; +}; +template +struct BEInt +{ + public: + inline void set (Type V) + { + v[0] = (V >> 24) & 0xFF; + v[1] = (V >> 16) & 0xFF; + v[2] = (V >> 8) & 0xFF; + v[3] = (V ) & 0xFF; + } + inline operator Type (void) const + { + return (v[0] << 24) + + (v[1] << 16) + + (v[2] << 8) + + (v[3] ); + } + private: uint8_t v[4]; +}; + + +/* + * Lazy struct and blob loaders. + */ + +/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */ +template +struct hb_lazy_loader_t +{ + inline void init (hb_face_t *face_) + { + face = face_; + instance = nullptr; + } + + inline void fini (void) + { + if (instance && instance != &Null(T)) + { + instance->fini(); + free (instance); + } + } + + inline const T* get (void) const + { + retry: + T *p = (T *) hb_atomic_ptr_get (&instance); + if (unlikely (!p)) + { + p = (T *) calloc (1, sizeof (T)); + if (unlikely (!p)) + p = const_cast (&Null(T)); + else + p->init (face); + if (unlikely (!hb_atomic_ptr_cmpexch (const_cast(&instance), nullptr, p))) + { + if (p != &Null(T)) + p->fini (); + goto retry; + } + } + return p; + } + + inline const T* operator-> (void) const + { + return get (); + } + + private: + hb_face_t *face; + mutable T *instance; +}; + +/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */ +template +struct hb_table_lazy_loader_t +{ + inline void init (hb_face_t *face_) + { + face = face_; + blob = nullptr; + } + + inline void fini (void) + { + hb_blob_destroy (blob); + } + + inline hb_blob_t* get_blob (void) const + { + retry: + hb_blob_t *b = (hb_blob_t *) hb_atomic_ptr_get (&blob); + if (unlikely (!b)) + { + b = OT::hb_sanitize_context_t().reference_table (face); + if (!hb_atomic_ptr_cmpexch (&blob, nullptr, b)) + { + hb_blob_destroy (b); + goto retry; + } + blob = b; + } + return b; + } + + inline const T* get (void) const + { + hb_blob_t *b = get_blob (); + return b->as (); + } + + inline const T* operator-> (void) const + { + return get(); + } + + private: + hb_face_t *face; + mutable hb_blob_t *blob; +}; + + +} /* namespace OT */ + + +#endif /* HB_MACHINERY_PRIVATE_HH */ diff --git a/src/hb-open-type-private.hh b/src/hb-open-type-private.hh index a143cc00d..bae2eed81 100644 --- a/src/hb-open-type-private.hh +++ b/src/hb-open-type-private.hh @@ -32,492 +32,12 @@ #include "hb-private.hh" #include "hb-blob-private.hh" #include "hb-face-private.hh" +#include "hb-machinery-private.hh" namespace OT { - -/* - * Casts - */ - -/* Cast to struct T, reference to reference */ -template -static inline const Type& CastR(const TObject &X) -{ return reinterpret_cast (X); } -template -static inline Type& CastR(TObject &X) -{ return reinterpret_cast (X); } - -/* Cast to struct T, pointer to pointer */ -template -static inline const Type* CastP(const TObject *X) -{ return reinterpret_cast (X); } -template -static inline Type* CastP(TObject *X) -{ return reinterpret_cast (X); } - -/* StructAtOffset(P,Ofs) returns the struct T& that is placed at memory - * location pointed to by P plus Ofs bytes. */ -template -static inline const Type& StructAtOffset(const void *P, unsigned int offset) -{ return * reinterpret_cast ((const char *) P + offset); } -template -static inline Type& StructAtOffset(void *P, unsigned int offset) -{ return * reinterpret_cast ((char *) P + offset); } - -/* StructAfter(X) returns the struct T& that is placed after X. - * Works with X of variable size also. X must implement get_size() */ -template -static inline const Type& StructAfter(const TObject &X) -{ return StructAtOffset(&X, X.get_size()); } -template -static inline Type& StructAfter(TObject &X) -{ return StructAtOffset(&X, X.get_size()); } - - - -/* - * Size checking - */ - -/* Check _assertion in a method environment */ -#define _DEFINE_INSTANCE_ASSERTION1(_line, _assertion) \ - inline void _instance_assertion_on_line_##_line (void) const \ - { \ - static_assert ((_assertion), ""); \ - ASSERT_INSTANCE_POD (*this); /* Make sure it's POD. */ \ - } -# define _DEFINE_INSTANCE_ASSERTION0(_line, _assertion) _DEFINE_INSTANCE_ASSERTION1 (_line, _assertion) -# define DEFINE_INSTANCE_ASSERTION(_assertion) _DEFINE_INSTANCE_ASSERTION0 (__LINE__, _assertion) - -/* Check that _code compiles in a method environment */ -#define _DEFINE_COMPILES_ASSERTION1(_line, _code) \ - inline void _compiles_assertion_on_line_##_line (void) const \ - { _code; } -# define _DEFINE_COMPILES_ASSERTION0(_line, _code) _DEFINE_COMPILES_ASSERTION1 (_line, _code) -# define DEFINE_COMPILES_ASSERTION(_code) _DEFINE_COMPILES_ASSERTION0 (__LINE__, _code) - - -#define DEFINE_SIZE_STATIC(size) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size)); \ - static const unsigned int static_size = (size); \ - static const unsigned int min_size = (size); \ - inline unsigned int get_size (void) const { return (size); } - -#define DEFINE_SIZE_UNION(size, _member) \ - DEFINE_INSTANCE_ASSERTION (0*sizeof(this->u._member.static_size) + sizeof(this->u._member) == (size)); \ - static const unsigned int min_size = (size) - -#define DEFINE_SIZE_MIN(size) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) >= (size)); \ - static const unsigned int min_size = (size) - -#define DEFINE_SIZE_ARRAY(size, array) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (array[0])); \ - DEFINE_COMPILES_ASSERTION ((void) array[0].static_size) \ - static const unsigned int min_size = (size) - -#define DEFINE_SIZE_ARRAY2(size, array1, array2) \ - DEFINE_INSTANCE_ASSERTION (sizeof (*this) == (size) + sizeof (this->array1[0]) + sizeof (this->array2[0])); \ - DEFINE_COMPILES_ASSERTION ((void) array1[0].static_size; (void) array2[0].static_size) \ - static const unsigned int min_size = (size) - - - -/* - * Dispatch - */ - -template -struct hb_dispatch_context_t -{ - static const unsigned int max_debug_depth = MaxDebugDepth; - typedef Return return_t; - template - inline bool may_dispatch (const T *obj, const F *format) { return true; } - static return_t no_dispatch_return_value (void) { return Context::default_return_value (); } -}; - - -/* - * Sanitize - */ - -/* This limits sanitizing time on really broken fonts. */ -#ifndef HB_SANITIZE_MAX_EDITS -#define HB_SANITIZE_MAX_EDITS 32 -#endif -#ifndef HB_SANITIZE_MAX_OPS_FACTOR -#define HB_SANITIZE_MAX_OPS_FACTOR 8 -#endif -#ifndef HB_SANITIZE_MAX_OPS_MIN -#define HB_SANITIZE_MAX_OPS_MIN 16384 -#endif - -struct hb_sanitize_context_t : - hb_dispatch_context_t -{ - inline hb_sanitize_context_t (void) : - debug_depth (0), - start (nullptr), end (nullptr), - writable (false), edit_count (0), max_ops (0), - blob (nullptr), - num_glyphs (65536), - num_glyphs_set (false) {} - - inline const char *get_name (void) { return "SANITIZE"; } - template - inline bool may_dispatch (const T *obj, const F *format) - { return format->sanitize (this); } - template - inline return_t dispatch (const T &obj) { return obj.sanitize (this); } - static return_t default_return_value (void) { return true; } - static return_t no_dispatch_return_value (void) { return false; } - bool stop_sublookup_iteration (const return_t r) const { return !r; } - - inline void init (hb_blob_t *b) - { - this->blob = hb_blob_reference (b); - this->writable = false; - } - - inline void set_num_glyphs (unsigned int num_glyphs_) - { - num_glyphs = num_glyphs_; - num_glyphs_set = true; - } - inline unsigned int get_num_glyphs (void) { return num_glyphs; } - - inline void start_processing (void) - { - this->start = hb_blob_get_data (this->blob, nullptr); - this->end = this->start + this->blob->length; - assert (this->start <= this->end); /* Must not overflow. */ - this->max_ops = MAX ((unsigned int) (this->end - this->start) * HB_SANITIZE_MAX_OPS_FACTOR, - (unsigned) HB_SANITIZE_MAX_OPS_MIN); - this->edit_count = 0; - this->debug_depth = 0; - - DEBUG_MSG_LEVEL (SANITIZE, start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - } - - inline void end_processing (void) - { - DEBUG_MSG_LEVEL (SANITIZE, this->start, 0, -1, - "end [%p..%p] %u edit requests", - this->start, this->end, this->edit_count); - - hb_blob_destroy (this->blob); - this->blob = nullptr; - this->start = this->end = nullptr; - } - - inline bool check_range (const void *base, unsigned int len) const - { - const char *p = (const char *) base; - bool ok = this->max_ops-- > 0 && - this->start <= p && - p <= this->end && - (unsigned int) (this->end - p) >= len; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "check_range [%p..%p] (%d bytes) in [%p..%p] -> %s", - p, p + len, len, - this->start, this->end, - ok ? "OK" : "OUT-OF-RANGE"); - - return likely (ok); - } - - inline bool check_array (const void *base, unsigned int record_size, unsigned int len) const - { - const char *p = (const char *) base; - bool overflows = hb_unsigned_mul_overflows (len, record_size); - unsigned int array_size = record_size * len; - bool ok = !overflows && this->check_range (base, array_size); - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "check_array [%p..%p] (%d*%d=%d bytes) in [%p..%p] -> %s", - p, p + (record_size * len), record_size, len, (unsigned int) array_size, - this->start, this->end, - overflows ? "OVERFLOWS" : ok ? "OK" : "OUT-OF-RANGE"); - - return likely (ok); - } - - template - inline bool check_struct (const Type *obj) const - { - return likely (this->check_range (obj, obj->min_size)); - } - - inline bool may_edit (const void *base, unsigned int len) - { - if (this->edit_count >= HB_SANITIZE_MAX_EDITS) - return false; - - const char *p = (const char *) base; - this->edit_count++; - - DEBUG_MSG_LEVEL (SANITIZE, p, this->debug_depth+1, 0, - "may_edit(%u) [%p..%p] (%d bytes) in [%p..%p] -> %s", - this->edit_count, - p, p + len, len, - this->start, this->end, - this->writable ? "GRANTED" : "DENIED"); - - return this->writable; - } - - template - inline bool try_set (const Type *obj, const ValueType &v) { - if (this->may_edit (obj, obj->static_size)) { - const_cast (obj)->set (v); - return true; - } - return false; - } - - template - inline hb_blob_t *sanitize_blob (hb_blob_t *blob) - { - bool sane; - - init (blob); - - retry: - DEBUG_MSG_FUNC (SANITIZE, start, "start"); - - start_processing (); - - if (unlikely (!start)) - { - end_processing (); - return blob; - } - - Type *t = CastP (const_cast (start)); - - sane = t->sanitize (this); - if (sane) - { - if (edit_count) - { - DEBUG_MSG_FUNC (SANITIZE, start, "passed first round with %d edits; going for second round", edit_count); - - /* sanitize again to ensure no toe-stepping */ - edit_count = 0; - sane = t->sanitize (this); - if (edit_count) { - DEBUG_MSG_FUNC (SANITIZE, start, "requested %d edits in second round; FAILLING", edit_count); - sane = false; - } - } - } - else - { - if (edit_count && !writable) { - start = hb_blob_get_data_writable (blob, nullptr); - end = start + blob->length; - - if (start) - { - writable = true; - /* ok, we made it writable by relocating. try again */ - DEBUG_MSG_FUNC (SANITIZE, start, "retry"); - goto retry; - } - } - } - - end_processing (); - - DEBUG_MSG_FUNC (SANITIZE, start, sane ? "PASSED" : "FAILED"); - if (sane) - { - blob->lock (); - return blob; - } - else - { - hb_blob_destroy (blob); - return hb_blob_get_empty (); - } - } - - template - inline hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag) - { - if (!num_glyphs_set) - set_num_glyphs (face->get_num_glyphs ()); - return sanitize_blob (face->reference_table (tableTag)); - } - - mutable unsigned int debug_depth; - const char *start, *end; - private: - bool writable; - unsigned int edit_count; - mutable int max_ops; - hb_blob_t *blob; - unsigned int num_glyphs; - bool num_glyphs_set; -}; - - -/* - * Serialize - */ - - -struct hb_serialize_context_t -{ - inline hb_serialize_context_t (void *start_, unsigned int size) - { - this->start = (char *) start_; - this->end = this->start + size; - - this->ran_out_of_room = false; - this->head = this->start; - this->debug_depth = 0; - } - - template - inline Type *start_serialize (void) - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1, - "start [%p..%p] (%lu bytes)", - this->start, this->end, - (unsigned long) (this->end - this->start)); - - return start_embed (); - } - - inline void end_serialize (void) - { - DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, - "end [%p..%p] serialized %d bytes; %s", - this->start, this->end, - (int) (this->head - this->start), - this->ran_out_of_room ? "RAN OUT OF ROOM" : "did not ran out of room"); - - } - - template - inline Type *copy (void) - { - assert (!this->ran_out_of_room); - unsigned int len = this->head - this->start; - void *p = malloc (len); - if (p) - memcpy (p, this->start, len); - return reinterpret_cast (p); - } - - template - inline Type *allocate_size (unsigned int size) - { - if (unlikely (this->ran_out_of_room || this->end - this->head < ptrdiff_t (size))) { - this->ran_out_of_room = true; - return nullptr; - } - memset (this->head, 0, size); - char *ret = this->head; - this->head += size; - return reinterpret_cast (ret); - } - - template - inline Type *allocate_min (void) - { - return this->allocate_size (Type::min_size); - } - - template - inline Type *start_embed (void) - { - Type *ret = reinterpret_cast (this->head); - return ret; - } - - template - inline Type *embed (const Type &obj) - { - unsigned int size = obj.get_size (); - Type *ret = this->allocate_size (size); - if (unlikely (!ret)) return nullptr; - memcpy (ret, obj, size); - return ret; - } - - template - inline Type *extend_min (Type &obj) - { - unsigned int size = obj.min_size; - assert (this->start <= (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; - return reinterpret_cast (&obj); - } - - template - inline Type *extend (Type &obj) - { - unsigned int size = obj.get_size (); - assert (this->start < (char *) &obj && (char *) &obj <= this->head && (char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size (((char *) &obj) + size - this->head))) return nullptr; - return reinterpret_cast (&obj); - } - - inline void truncate (void *new_head) - { - assert (this->start < new_head && new_head <= this->head); - this->head = (char *) new_head; - } - - unsigned int debug_depth; - char *start, *end, *head; - bool ran_out_of_room; -}; - -template -struct Supplier -{ - inline Supplier (const Type *array, unsigned int len_, unsigned int stride_=sizeof(Type)) - { - head = array; - len = len_; - stride = stride_; - } - inline const Type operator [] (unsigned int i) const - { - if (unlikely (i >= len)) return Type (); - return * (const Type *) (const void *) ((const char *) head + stride * i); - } - - inline Supplier & operator += (unsigned int count) - { - if (unlikely (count > len)) - count = len; - len -= count; - head = (const Type *) (const void *) ((const char *) head + stride * count); - return *this; - } - - private: - inline Supplier (const Supplier &); /* Disallow copy */ - inline Supplier& operator= (const Supplier &); /* Disallow copy */ - - unsigned int len; - unsigned int stride; - const Type *head; -}; - - /* * * The OpenType Font File: Data Types @@ -531,78 +51,6 @@ struct Supplier * Int types */ - -template struct BEInt; - -template -struct BEInt -{ - public: - inline void set (Type V) - { - v = V; - } - inline operator Type (void) const - { - return v; - } - private: uint8_t v; -}; -template -struct BEInt -{ - public: - inline void set (Type V) - { - v[0] = (V >> 8) & 0xFF; - v[1] = (V ) & 0xFF; - } - inline operator Type (void) const - { - return (v[0] << 8) - + (v[1] ); - } - private: uint8_t v[2]; -}; -template -struct BEInt -{ - public: - inline void set (Type V) - { - v[0] = (V >> 16) & 0xFF; - v[1] = (V >> 8) & 0xFF; - v[2] = (V ) & 0xFF; - } - inline operator Type (void) const - { - return (v[0] << 16) - + (v[1] << 8) - + (v[2] ); - } - private: uint8_t v[3]; -}; -template -struct BEInt -{ - public: - inline void set (Type V) - { - v[0] = (V >> 24) & 0xFF; - v[1] = (V >> 16) & 0xFF; - v[2] = (V >> 8) & 0xFF; - v[3] = (V ) & 0xFF; - } - inline operator Type (void) const - { - return (v[0] << 24) - + (v[1] << 16) - + (v[2] << 8) - + (v[3] ); - } - private: uint8_t v[4]; -}; - /* Integer types in big-endian order and no alignment requirement */ template struct IntType @@ -773,7 +221,6 @@ struct FixedVersion }; - /* * Template subclasses of Offset that do the dereferencing. * Use: (base+offset) @@ -839,7 +286,6 @@ static inline Type& operator + (Base &base, OffsetTo &offset) * Array Types */ - /* TODO Use it in ArrayOf, HeadlessArrayOf, and other places around the code base?? */ template struct UnsizedArrayOf @@ -921,7 +367,6 @@ struct UnsizedOffsetListOf : UnsizedOffsetArrayOf } }; - /* An array with a number of elements. */ template struct ArrayOf @@ -1074,7 +519,6 @@ struct OffsetListOf : OffsetArrayOf } }; - /* An array starting at second element. */ template struct HeadlessArrayOf @@ -1139,10 +583,7 @@ struct HeadlessArrayOf DEFINE_SIZE_ARRAY (sizeof (LenType), arrayZ); }; - -/* - * An array with sorted elements. Supports binary searching. - */ +/* An array with sorted elements. Supports binary searching. */ template struct SortedArrayOf : ArrayOf { @@ -1167,10 +608,7 @@ struct SortedArrayOf : ArrayOf } }; -/* - * Binary-search arrays - */ - +/* Binary-search arrays */ struct BinSearchHeader { inline operator uint32_t (void) const { return len; } @@ -1206,107 +644,6 @@ template struct BinSearchArrayOf : SortedArrayOf {}; -/* Lazy struct and blob loaders. */ - -/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */ -template -struct hb_lazy_loader_t -{ - inline void init (hb_face_t *face_) - { - face = face_; - instance = nullptr; - } - - inline void fini (void) - { - if (instance && instance != &Null(T)) - { - instance->fini(); - free (instance); - } - } - - inline const T* get (void) const - { - retry: - T *p = (T *) hb_atomic_ptr_get (&instance); - if (unlikely (!p)) - { - p = (T *) calloc (1, sizeof (T)); - if (unlikely (!p)) - p = const_cast (&Null(T)); - else - p->init (face); - if (unlikely (!hb_atomic_ptr_cmpexch (const_cast(&instance), nullptr, p))) - { - if (p != &Null(T)) - p->fini (); - goto retry; - } - } - return p; - } - - inline const T* operator-> (void) const - { - return get (); - } - - private: - hb_face_t *face; - mutable T *instance; -}; - -/* Logic is shared between hb_lazy_loader_t and hb_table_lazy_loader_t */ -template -struct hb_table_lazy_loader_t -{ - inline void init (hb_face_t *face_) - { - face = face_; - blob = nullptr; - } - - inline void fini (void) - { - hb_blob_destroy (blob); - } - - inline hb_blob_t* get_blob (void) const - { - retry: - hb_blob_t *b = (hb_blob_t *) hb_atomic_ptr_get (&blob); - if (unlikely (!b)) - { - b = OT::hb_sanitize_context_t().reference_table (face); - if (!hb_atomic_ptr_cmpexch (&blob, nullptr, b)) - { - hb_blob_destroy (b); - goto retry; - } - blob = b; - } - return b; - } - - inline const T* get (void) const - { - hb_blob_t *b = get_blob (); - return b->as (); - } - - inline const T* operator-> (void) const - { - return get(); - } - - private: - hb_face_t *face; - mutable hb_blob_t *blob; -}; - - } /* namespace OT */