diff options
Diffstat (limited to 'Build/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh')
-rw-r--r-- | Build/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh | 618 |
1 files changed, 4 insertions, 614 deletions
diff --git a/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh b/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh index ffa423d2fe4..5a2087e4aa9 100644 --- a/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh +++ b/Build/source/libs/harfbuzz/harfbuzz-src/src/hb-machinery.hh @@ -32,8 +32,9 @@ #include "hb.hh" #include "hb-blob.hh" -#include "hb-array.hh" -#include "hb-vector.hh" +#include "hb-dispatch.hh" +#include "hb-sanitize.hh" +#include "hb-serialize.hh" /* @@ -143,617 +144,6 @@ static inline Type& StructAfter(TObject &X) DEFINE_SIZE_ARRAY(size, array) -/* - * Dispatch - */ - -template <typename Context, typename Return, unsigned int MaxDebugDepth> -struct hb_dispatch_context_t -{ - static constexpr unsigned max_debug_depth = MaxDebugDepth; - typedef Return return_t; - template <typename T, typename F> - bool may_dispatch (const T *obj HB_UNUSED, const F *format HB_UNUSED) { return true; } - static return_t no_dispatch_return_value () { return Context::default_return_value (); } - static bool stop_sublookup_iteration (const return_t r HB_UNUSED) { return false; } -}; - - -/* - * Sanitize - * - * - * === Introduction === - * - * The sanitize machinery is at the core of our zero-cost font loading. We - * mmap() font file into memory and create a blob out of it. Font subtables - * are returned as a readonly sub-blob of the main font blob. These table - * blobs are then sanitized before use, to ensure invalid memory access does - * not happen. The toplevel sanitize API use is like, eg. to load the 'head' - * table: - * - * hb_blob_t *head_blob = hb_sanitize_context_t ().reference_table<OT::head> (face); - * - * The blob then can be converted to a head table struct with: - * - * const head *head_table = head_blob->as<head> (); - * - * What the reference_table does is, to call hb_face_reference_table() to load - * the table blob, sanitize it and return either the sanitized blob, or empty - * blob if sanitization failed. The blob->as() function returns the null - * object of its template type argument if the blob is empty. Otherwise, it - * just casts the blob contents to the desired type. - * - * Sanitizing a blob of data with a type T works as follows (with minor - * simplification): - * - * - Cast blob content to T*, call sanitize() method of it, - * - If sanitize succeeded, return blob. - * - Otherwise, if blob is not writable, try making it writable, - * or copy if cannot be made writable in-place, - * - Call sanitize() again. Return blob if sanitize succeeded. - * - Return empty blob otherwise. - * - * - * === The sanitize() contract === - * - * The sanitize() method of each object type shall return true if it's safe to - * call other methods of the object, and false otherwise. - * - * Note that what sanitize() checks for might align with what the specification - * describes as valid table data, but does not have to be. In particular, we - * do NOT want to be pedantic and concern ourselves with validity checks that - * are irrelevant to our use of the table. On the contrary, we want to be - * lenient with error handling and accept invalid data to the extent that it - * does not impose extra burden on us. - * - * Based on the sanitize contract, one can see that what we check for depends - * on how we use the data in other table methods. Ie. if other table methods - * assume that offsets do NOT point out of the table data block, then that's - * something sanitize() must check for (GSUB/GPOS/GDEF/etc work this way). On - * the other hand, if other methods do such checks themselves, then sanitize() - * does not have to bother with them (glyf/local work this way). The choice - * depends on the table structure and sanitize() performance. For example, to - * check glyf/loca offsets in sanitize() would cost O(num-glyphs). We try hard - * to avoid such costs during font loading. By postponing such checks to the - * actual glyph loading, we reduce the sanitize cost to O(1) and total runtime - * cost to O(used-glyphs). As such, this is preferred. - * - * The same argument can be made re GSUB/GPOS/GDEF, but there, the table - * structure is so complicated that by checking all offsets at sanitize() time, - * we make the code much simpler in other methods, as offsets and referenced - * objects do not need to be validated at each use site. - */ - -/* 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 -#ifndef HB_SANITIZE_MAX_OPS_MAX -#define HB_SANITIZE_MAX_OPS_MAX 0x3FFFFFFF -#endif - -struct hb_sanitize_context_t : - hb_dispatch_context_t<hb_sanitize_context_t, bool, HB_DEBUG_SANITIZE> -{ - hb_sanitize_context_t () : - debug_depth (0), - start (nullptr), end (nullptr), - max_ops (0), - writable (false), edit_count (0), - blob (nullptr), - num_glyphs (65536), - num_glyphs_set (false) {} - - const char *get_name () { return "SANITIZE"; } - template <typename T, typename F> - bool may_dispatch (const T *obj HB_UNUSED, const F *format) - { return format->sanitize (this); } - template <typename T> - return_t dispatch (const T &obj) { return obj.sanitize (this); } - static return_t default_return_value () { return true; } - static return_t no_dispatch_return_value () { return false; } - bool stop_sublookup_iteration (const return_t r) const { return !r; } - - void init (hb_blob_t *b) - { - this->blob = hb_blob_reference (b); - this->writable = false; - } - - void set_num_glyphs (unsigned int num_glyphs_) - { - num_glyphs = num_glyphs_; - num_glyphs_set = true; - } - unsigned int get_num_glyphs () { return num_glyphs; } - - void set_max_ops (int max_ops_) { max_ops = max_ops_; } - - template <typename T> - void set_object (const T *obj) - { - reset_object (); - - if (!obj) return; - - const char *obj_start = (const char *) obj; - if (unlikely (obj_start < this->start || this->end <= obj_start)) - this->start = this->end = nullptr; - else - { - this->start = obj_start; - this->end = obj_start + MIN<uintptr_t> (this->end - obj_start, obj->get_size ()); - } - } - - void reset_object () - { - this->start = this->blob->data; - this->end = this->start + this->blob->length; - assert (this->start <= this->end); /* Must not overflow. */ - } - - void start_processing () - { - reset_object (); - 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)); - } - - void end_processing () - { - 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; - } - - bool check_range (const void *base, - unsigned int len) const - { - const char *p = (const char *) base; - bool ok = !len || - (this->start <= p && - p <= this->end && - (unsigned int) (this->end - p) >= len && - this->max_ops-- > 0); - - 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); - } - - template <typename T> - bool check_range (const T *base, - unsigned int a, - unsigned int b) const - { - return !hb_unsigned_mul_overflows (a, b) && - this->check_range (base, a * b); - } - - template <typename T> - bool check_range (const T *base, - unsigned int a, - unsigned int b, - unsigned int c) const - { - return !hb_unsigned_mul_overflows (a, b) && - this->check_range (base, a * b, c); - } - - template <typename T> - bool check_array (const T *base, unsigned int len) const - { - return this->check_range (base, len, hb_static_size (T)); - } - - template <typename T> - bool check_array (const T *base, - unsigned int a, - unsigned int b) const - { - return this->check_range (base, a, b, hb_static_size (T)); - } - - template <typename Type> - bool check_struct (const Type *obj) const - { return likely (this->check_range (obj, obj->min_size)); } - - 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 <typename Type, typename ValueType> - bool try_set (const Type *obj, const ValueType &v) - { - if (this->may_edit (obj, hb_static_size (Type))) - { - hb_assign (* const_cast<Type *> (obj), v); - return true; - } - return false; - } - - template <typename Type> - 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<Type> (const_cast<char *> (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) - { - hb_blob_make_immutable (blob); - return blob; - } - else - { - hb_blob_destroy (blob); - return hb_blob_get_empty (); - } - } - - template <typename Type> - hb_blob_t *reference_table (const hb_face_t *face, hb_tag_t tableTag = Type::tableTag) - { - if (!num_glyphs_set) - set_num_glyphs (hb_face_get_glyph_count (face)); - return sanitize_blob<Type> (hb_face_reference_table (face, tableTag)); - } - - mutable unsigned int debug_depth; - const char *start, *end; - mutable int max_ops; - private: - bool writable; - unsigned int edit_count; - hb_blob_t *blob; - unsigned int num_glyphs; - bool num_glyphs_set; -}; - -struct hb_sanitize_with_object_t -{ - template <typename T> - hb_sanitize_with_object_t (hb_sanitize_context_t *c, - const T& obj) : c (c) - { c->set_object (obj); } - ~hb_sanitize_with_object_t () - { c->reset_object (); } - - private: - hb_sanitize_context_t *c; -}; - - -/* - * Serialize - */ - -struct hb_serialize_context_t -{ - hb_serialize_context_t (void *start_, unsigned int size) - { - this->start = (char *) start_; - this->end = this->start + size; - reset (); - } - - bool in_error () const { return !this->successful; } - - void reset () - { - this->successful = true; - this->head = this->start; - this->debug_depth = 0; - } - - bool propagate_error (bool e) - { return this->successful = this->successful && e; } - template <typename T> bool propagate_error (const T &obj) - { return this->successful = this->successful && !obj.in_error (); } - template <typename T> bool propagate_error (const T *obj) - { return this->successful = this->successful && !obj->in_error (); } - template <typename T1, typename T2> bool propagate_error (T1 &o1, T2 &o2) - { return propagate_error (o1) && propagate_error (o2); } - template <typename T1, typename T2> bool propagate_error (T1 *o1, T2 *o2) - { return propagate_error (o1) && propagate_error (o2); } - template <typename T1, typename T2, typename T3> - bool propagate_error (T1 &o1, T2 &o2, T3 &o3) - { return propagate_error (o1) && propagate_error (o2, o3); } - template <typename T1, typename T2, typename T3> - bool propagate_error (T1 *o1, T2 *o2, T3 *o3) - { return propagate_error (o1) && propagate_error (o2, o3); } - - /* To be called around main operation. */ - template <typename Type> - Type *start_serialize () - { - 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<Type> (); - } - void end_serialize () - { - 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->successful ? "successful" : "UNSUCCESSFUL"); - } - - unsigned int length () const { return this->head - this->start; } - - void align (unsigned int alignment) - { - unsigned int l = length () % alignment; - if (l) - allocate_size<void> (alignment - l); - } - - template <typename Type> - Type *start_embed (const Type *_ HB_UNUSED = nullptr) const - { - Type *ret = reinterpret_cast<Type *> (this->head); - return ret; - } - - template <typename Type> - Type *allocate_size (unsigned int size) - { - if (unlikely (!this->successful || this->end - this->head < ptrdiff_t (size))) { - this->successful = false; - return nullptr; - } - memset (this->head, 0, size); - char *ret = this->head; - this->head += size; - return reinterpret_cast<Type *> (ret); - } - - template <typename Type> - Type *allocate_min () - { - return this->allocate_size<Type> (Type::min_size); - } - - template <typename Type> - Type *embed (const Type &obj) - { - unsigned int size = obj.get_size (); - Type *ret = this->allocate_size<Type> (size); - if (unlikely (!ret)) return nullptr; - memcpy (ret, &obj, size); - return ret; - } - template <typename Type> - hb_serialize_context_t &operator << (const Type &obj) { embed (obj); return *this; } - - template <typename Type> - Type *extend_size (Type &obj, unsigned int size) - { - assert (this->start <= (char *) &obj); - assert ((char *) &obj <= this->head); - assert ((char *) &obj + size >= this->head); - if (unlikely (!this->allocate_size<Type> (((char *) &obj) + size - this->head))) return nullptr; - return reinterpret_cast<Type *> (&obj); - } - - template <typename Type> - Type *extend_min (Type &obj) { return extend_size (obj, obj.min_size); } - - template <typename Type> - Type *extend (Type &obj) { return extend_size (obj, obj.get_size ()); } - - /* Output routines. */ - template <typename Type> - Type *copy () const - { - assert (this->successful); - unsigned int len = this->head - this->start; - void *p = malloc (len); - if (p) - memcpy (p, this->start, len); - return reinterpret_cast<Type *> (p); - } - hb_bytes_t copy_bytes () const - { - assert (this->successful); - unsigned int len = this->head - this->start; - void *p = malloc (len); - if (p) - memcpy (p, this->start, len); - else - return hb_bytes_t (); - return hb_bytes_t ((char *) p, len); - } - hb_blob_t *copy_blob () const - { - assert (this->successful); - return hb_blob_create (this->start, - this->head - this->start, - HB_MEMORY_MODE_DUPLICATE, - nullptr, nullptr); - } - - public: - unsigned int debug_depth; - char *start, *end, *head; - bool successful; -}; - - - -/* - * Big-endian integers. - */ - -template <typename Type, int Bytes> struct BEInt; - -template <typename Type> -struct BEInt<Type, 1> -{ - public: - void set (Type V) { v = V; } - operator Type () const { return v; } - private: uint8_t v; -}; -template <typename Type> -struct BEInt<Type, 2> -{ - public: - void set (Type V) - { - v[0] = (V >> 8) & 0xFF; - v[1] = (V ) & 0xFF; - } - operator Type () const - { -#if ((defined(__GNUC__) && __GNUC__ >= 5) || defined(__clang__)) && \ - defined(__BYTE_ORDER) && \ - (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN) - /* Spoon-feed the compiler a big-endian integer with alignment 1. - * https://github.com/harfbuzz/harfbuzz/pull/1398 */ - struct __attribute__((packed)) packed_uint16_t { uint16_t v; }; -#if __BYTE_ORDER == __LITTLE_ENDIAN - return __builtin_bswap16 (((packed_uint16_t *) this)->v); -#else /* __BYTE_ORDER == __BIG_ENDIAN */ - return ((packed_uint16_t *) this)->v; -#endif -#endif - return (v[0] << 8) - + (v[1] ); - } - private: uint8_t v[2]; -}; -template <typename Type> -struct BEInt<Type, 3> -{ - public: - void set (Type V) - { - v[0] = (V >> 16) & 0xFF; - v[1] = (V >> 8) & 0xFF; - v[2] = (V ) & 0xFF; - } - operator Type () const - { - return (v[0] << 16) - + (v[1] << 8) - + (v[2] ); - } - private: uint8_t v[3]; -}; -template <typename Type> -struct BEInt<Type, 4> -{ - public: - typedef Type type; - void set (Type V) - { - v[0] = (V >> 24) & 0xFF; - v[1] = (V >> 16) & 0xFF; - v[2] = (V >> 8) & 0xFF; - v[3] = (V ) & 0xFF; - } - operator Type () const - { - return (v[0] << 24) - + (v[1] << 16) - + (v[2] << 8) - + (v[3] ); - } - private: uint8_t v[4]; -}; - /* * Lazy loaders. @@ -816,7 +206,7 @@ struct hb_lazy_loader_t : hb_data_wrapper_t<Data, WheresData> const Returned * operator -> () const { return get (); } const Returned & operator * () const { return *get (); } - explicit_operator bool () const + explicit operator bool () const { return get_stored () != Funcs::get_null (); } template <typename C> operator const C * () const { return get (); } |