diff --git a/src/hb-algs.hh b/src/hb-algs.hh index 128e49036..adf5c483f 100644 --- a/src/hb-algs.hh +++ b/src/hb-algs.hh @@ -40,12 +40,12 @@ static const struct //{ return hb_deref_pointer (v).hash (); } /* Instead, the following ugly soution: */ template + hb_enable_if (!hb_is_integer (hb_remove_const (hb_remove_reference (T))) && !hb_is_pointer (T))> uint32_t operator () (T&& v) const { return v.hash (); } template uint32_t operator () (const T *v) const - { return hb_hash (v); } + { return operator() (*v); } template diff --git a/src/hb-array.hh b/src/hb-array.hh index ab453f5a2..16f53cc8f 100644 --- a/src/hb-array.hh +++ b/src/hb-array.hh @@ -79,6 +79,8 @@ struct hb_array_t : hb_iter_with_fallback_t, Type&> operator hb_array_t () { return hb_array_t (arrayZ, length); } template operator T * () const { return arrayZ; } + uint32_t hash () const; + /* * Compare, Sort, and Search. */ @@ -273,9 +275,20 @@ template inline hb_sorted_array_t hb_sorted_array (T (&array_)[length_]) { return hb_sorted_array_t (array_); } +template +uint32_t hb_array_t::hash () const +{ + uint32_t h = 0; + for (unsigned i = 0; i < length; i++) + h ^= hb_hash (arrayZ[i]); + return h; +} typedef hb_array_t hb_bytes_t; typedef hb_array_t hb_ubytes_t; +/* TODO Specialize hashing for hb_bytes_t and hb_ubytes_t. */ +//template <> +//uint32_t hb_array_t::hash () const { return 0; } #endif /* HB_ARRAY_HH */ diff --git a/src/hb-map.hh b/src/hb-map.hh index d36515a74..f5c999170 100644 --- a/src/hb-map.hh +++ b/src/hb-map.hh @@ -34,6 +34,8 @@ * hb_hashmap_t */ +/* TODO if K/V is signed integer, -1 is not a good default. + * Don't know how to get to -MAX using bit work. */ template @@ -46,6 +48,10 @@ struct hb_hashmap_t static_assert (hb_is_integer (K) || hb_is_pointer (K), ""); static_assert (hb_is_integer (V) || hb_is_pointer (V), ""); + /* TODO If key type is a pointer, keep hash in item_t and use to: + * 1. avoid rehashing when resizing table, and + * 2. compare hash before comparing keys, for speed. + */ struct item_t { K key; @@ -82,14 +88,21 @@ struct hb_hashmap_t { free (items); items = nullptr; + population = occupancy = 0; } void fini () { - population = occupancy = 0; hb_object_fini (this); fini_shallow (); } + void reset () + { + /* TODO Keep array? */ + fini_shallow (); + init_shallow (); + } + bool in_error () const { return !successful; } bool resize () @@ -248,6 +261,10 @@ struct hb_hashmap_t } }; +/* + * hb_map_t + */ + struct hb_map_t : hb_hashmap_t +hb_remove_reference (T)&& hb_move (T&& t) { return (hb_remove_reference (T)&&) (t); } + +template +T&& hb_forward (hb_remove_reference (T)& t) { return (T&&) t; } +template +T&& hb_forward (hb_remove_reference (T)&& t) { return (T&&) t; } + + /* Void! For when we need a expression-type of void. */ struct hb_void_t { typedef void value; }; diff --git a/src/hb-serialize.hh b/src/hb-serialize.hh index f5e4df521..ab07080b8 100644 --- a/src/hb-serialize.hh +++ b/src/hb-serialize.hh @@ -31,6 +31,7 @@ #include "hb.hh" #include "hb-blob.hh" +#include "hb-map.hh" /* @@ -39,20 +40,70 @@ struct hb_serialize_context_t { + typedef unsigned objidx_t; + + struct range_t + { + char *head, *tail; + }; + + struct object_t : range_t + { + void fini () { links.fini (); } + + bool operator == (const object_t &o) const + { + return (tail - head == o.tail - o.head) + && (links.length != o.links.length) + && 0 == memcmp (head, o.head, tail - head) + && 0 == memcmp (&links, &o.links, links.get_size ()); + } + uint32_t hash () const + { + return hb_bytes_t (head, tail - head).hash () ^ + links.as_bytes ().hash (); + } + + struct link_t + { + bool wide: 1; + unsigned offset : 31; + objidx_t objidx; + }; + + hb_vector_t links; + }; + + range_t snapshot () { range_t s = {head, tail} ; return s; } + + hb_serialize_context_t (void *start_, unsigned int size) { this->start = (char *) start_; this->end = this->start + size; reset (); } + ~hb_serialize_context_t () + { + current.fini_deep (); + packed.fini_deep (); + packed_map.fini (); + } bool in_error () const { return !this->successful; } void reset () { this->successful = true; + this->ran_out_of_room = false; this->head = this->start; + this->tail = this->end; this->debug_depth = 0; + + this->current.reset (); + this->packed.reset (); + this->packed.push ()->head = this->end; + this->packed_map.reset (); } bool propagate_error (bool e) @@ -61,15 +112,10 @@ struct hb_serialize_context_t { return this->successful = this->successful && !obj.in_error (); } template bool propagate_error (const T *obj) { return this->successful = this->successful && !obj->in_error (); } - template bool propagate_error (T1 &o1, T2 &o2) - { return propagate_error (o1) && propagate_error (o2); } - template bool propagate_error (T1 *o1, T2 *o2) + template bool propagate_error (T1 &&o1, T2 &&o2) { return propagate_error (o1) && propagate_error (o2); } template - bool propagate_error (T1 &o1, T2 &o2, T3 &o3) - { return propagate_error (o1) && propagate_error (o2, o3); } - template - bool propagate_error (T1 *o1, T2 *o2, T3 *o3) + bool propagate_error (T1 &&o1, T2 &&o2, T3 &&o3) { return propagate_error (o1) && propagate_error (o2, o3); } /* To be called around main operation. */ @@ -81,18 +127,103 @@ struct hb_serialize_context_t this->start, this->end, (unsigned long) (this->end - this->start)); - return start_embed (); + assert (!current.length); + return push (); } void end_serialize () { DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, -1, - "end [%p..%p] serialized %d bytes; %s", + "end [%p..%p] serialized %u bytes; %s", this->start, this->end, - (int) (this->head - this->start), + (unsigned) (this->head - this->start), this->successful ? "successful" : "UNSUCCESSFUL"); + + /* TODO Propagate errors. */ + + assert (current.length == 1); + + /* Only "pack" if there exist other objects... Otherwise, don't bother. + * Saves a move. */ + if (packed.length == 1) + return; + + pop_pack (); + + link (); } - unsigned int length () const { return this->head - this->start; } + template + Type *push () + { + object_t obj; + obj.head = head; + obj.tail = tail; + current.push (obj); + return start_embed (); + } + void pop_discard () + { + revert (current.pop ()); + } + objidx_t pop_pack () + { + object_t obj = current.pop (); + obj.tail = head; + unsigned len = obj.tail - obj.head; + + objidx_t objidx = packed_map.get (&obj); + if (objidx) + { + obj.fini (); + return objidx; + } + + tail -= len; + memmove (tail, obj.head, len); + head = obj.head; + + if (!len) + return 0; + + obj.head = tail; + obj.tail = tail + len; + + object_t *key = packed.push (hb_move (obj)); + + /* TODO Handle error. */ + if (unlikely (packed.in_error ())) + return 0; + + objidx = packed.length - 1; + + packed_map.set (key, objidx); + + return objidx; + } + + void revert (range_t snap) + { + assert (snap.head <= head); + assert (tail <= snap.tail); + head = snap.head; + tail = snap.tail; + discard_stale_objects (); + } + + void discard_stale_objects () + { + while (packed.length > 1 && + packed.tail ().head < tail) + packed.pop (); + assert (packed.tail ().head == tail); + } + + void link () + { + // XXX + } + + unsigned int length () const { return this->head - current.tail ().head; } void align (unsigned int alignment) { @@ -111,7 +242,11 @@ struct hb_serialize_context_t template Type *allocate_size (unsigned int size) { - if (unlikely (!this->successful || this->end - this->head < ptrdiff_t (size))) { + if (unlikely (!this->successful)) return nullptr; + + if (this->tail - this->head < ptrdiff_t (size)) + { + this->ran_out_of_room = true; this->successful = false; return nullptr; } @@ -156,40 +291,49 @@ struct hb_serialize_context_t Type *extend (Type &obj) { return extend_size (obj, obj.get_size ()); } /* Output routines. */ - template - 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 (p); - } hb_bytes_t copy_bytes () const { assert (this->successful); - unsigned int len = this->head - this->start; - void *p = malloc (len); + /* Copy both items from head side and tail side... */ + unsigned int len = (this->head - this->start) + + (this->end - this->tail); + char *p = (char *) malloc (len); if (p) - memcpy (p, this->start, len); + { + memcpy (p, this->start, this->head - this->start); + memcpy (p + (this->head - this->start), this->tail, this->end - this->tail); + } else return hb_bytes_t (); - return hb_bytes_t ((char *) p, len); + return hb_bytes_t (p, len); } + template + Type *copy () const + { return reinterpret_cast ((char *) copy_bytes ().arrayZ); } 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); + hb_bytes_t b = copy_bytes (); + return hb_blob_create (b.arrayZ, b.length, + HB_MEMORY_MODE_WRITABLE, + (char *) b.arrayZ, free); } - public: + public: /* TODO Make private. */ + char *start, *head, *tail, *end; unsigned int debug_depth; - char *start, *end, *head; bool successful; + bool ran_out_of_room; + + private: + + /* Stack of currently under construction objects. */ + hb_vector_t current; + + /* Stack of packed objects. Object 0 is always nil object. */ + hb_vector_t packed; + + /* Map view of packed objects. */ + hb_hashmap_t packed_map; }; diff --git a/src/hb-subset.cc b/src/hb-subset.cc index cff7c566f..b73638ced 100644 --- a/src/hb-subset.cc +++ b/src/hb-subset.cc @@ -85,7 +85,7 @@ _subset2 (hb_subset_plan_t *plan) hb_serialize_context_t serializer ((void *) buf, buf_size); hb_subset_context_t c (plan, &serializer); result = table->subset (&c); - if (serializer.in_error ()) + if (serializer.ran_out_of_room) { buf_size += (buf_size >> 1) + 32; DEBUG_MSG(SUBSET, nullptr, "OT::%c%c%c%c ran out of room; reallocating to %u bytes.", HB_UNTAG (tag), buf_size); @@ -96,6 +96,11 @@ _subset2 (hb_subset_plan_t *plan) } goto retry; } + if (serializer.in_error ()) + { + abort (); + } + if (result) { hb_blob_t *dest_blob = serializer.copy_blob (); diff --git a/src/hb-vector.hh b/src/hb-vector.hh index 0354845c4..4621b5134 100644 --- a/src/hb-vector.hh +++ b/src/hb-vector.hh @@ -38,8 +38,20 @@ struct hb_vector_t typedef Type item_t; static constexpr unsigned item_size = hb_static_size (Type); - HB_NO_COPY_ASSIGN_TEMPLATE (hb_vector_t, Type); hb_vector_t () { init (); } + hb_vector_t (const hb_vector_t &o) + { + init (); + alloc (o.length); + hb_iter (o) | hb_sink (this); + } + hb_vector_t (hb_vector_t &&o) + { + allocated = o.allocated; + length = o.length; + arrayZ_ = o.arrayZ_; + o.init (); + } ~hb_vector_t () { fini (); } unsigned int length; @@ -69,6 +81,30 @@ struct hb_vector_t fini (); } + void reset () { resize (0); } + + hb_vector_t& operator = (const hb_vector_t &o) + { + reset (); + alloc (o.length); + hb_iter (o) | hb_sink (this); + return *this; + } + hb_vector_t& operator = (hb_vector_t &&o) + { + fini (); + allocated = o.allocated; + length = o.length; + arrayZ_ = o.arrayZ_; + o.init (); + return *this; + } + + hb_bytes_t as_bytes () const { return hb_bytes_t ((const char *) arrayZ_, + length * item_size); } + + uint32_t hash () const { return as_bytes ().hash (); } + const Type * arrayZ () const { return arrayZ_; } Type * arrayZ () { return arrayZ_; } @@ -87,11 +123,15 @@ struct hb_vector_t return arrayZ()[i]; } + Type& tail () { return (*this)[length - 1]; } + const Type& tail () const { return (*this)[length - 1]; } + explicit operator bool () const { return length; } + unsigned get_size () const { return length * item_size; } /* Sink interface. */ template - hb_vector_t& operator << (const T& v) { push (v); return *this; } + hb_vector_t& operator << (T&& v) { push (hb_forward (v)); return *this; } hb_array_t< Type> as_array () { return hb_array (arrayZ(), length); } hb_array_t as_array () const { return hb_array (arrayZ(), length); } @@ -131,10 +171,10 @@ struct hb_vector_t return &arrayZ()[length - 1]; } template - Type *push (const T& v) + Type *push (T&& v) { Type *p = push (); - *p = v; + *p = hb_forward (v); return p; } @@ -188,10 +228,10 @@ struct hb_vector_t return true; } - void pop () + Type pop () { - if (!length) return; - length--; + if (!length) return Null(Type); + return hb_move (arrayZ()[--length]); /* Does this move actually work? */ } void remove (unsigned int i)