Merge branch 'master' into var-subset
This commit is contained in:
commit
f7700fc479
|
@ -40,12 +40,12 @@ static const struct
|
|||
//{ return hb_deref_pointer (v).hash (); }
|
||||
/* Instead, the following ugly soution: */
|
||||
template <typename T,
|
||||
hb_enable_if (!hb_is_integer (hb_remove_reference (T)) && !hb_is_pointer (T))>
|
||||
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 <typename T>
|
||||
uint32_t operator () (const T *v) const
|
||||
{ return hb_hash (v); }
|
||||
{ return operator() (*v); }
|
||||
|
||||
template <typename T,
|
||||
hb_enable_if (hb_is_integer (T))>
|
||||
|
|
|
@ -79,6 +79,8 @@ struct hb_array_t : hb_iter_with_fallback_t<hb_array_t<Type>, Type&>
|
|||
operator hb_array_t<const Type> () { return hb_array_t<const Type> (arrayZ, length); }
|
||||
template <typename T> operator T * () const { return arrayZ; }
|
||||
|
||||
uint32_t hash () const;
|
||||
|
||||
/*
|
||||
* Compare, Sort, and Search.
|
||||
*/
|
||||
|
@ -273,9 +275,20 @@ template <typename T, unsigned int length_> inline hb_sorted_array_t<T>
|
|||
hb_sorted_array (T (&array_)[length_])
|
||||
{ return hb_sorted_array_t<T> (array_); }
|
||||
|
||||
template <typename T>
|
||||
uint32_t hb_array_t<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<const char> hb_bytes_t;
|
||||
typedef hb_array_t<const unsigned char> hb_ubytes_t;
|
||||
|
||||
/* TODO Specialize hashing for hb_bytes_t and hb_ubytes_t. */
|
||||
//template <>
|
||||
//uint32_t hb_array_t<const char>::hash () const { return 0; }
|
||||
|
||||
#endif /* HB_ARRAY_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 <typename K, typename V,
|
||||
K kINVALID = hb_is_pointer (K) ? 0 : (K) -1,
|
||||
V vINVALID = hb_is_pointer (V) ? 0 : (V) -1>
|
||||
|
@ -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_codepoint_t,
|
||||
hb_codepoint_t,
|
||||
HB_MAP_VALUE_INVALID,
|
||||
|
|
|
@ -72,6 +72,17 @@ static const struct
|
|||
} hb_deref_pointer HB_UNUSED;
|
||||
|
||||
|
||||
/* std::move and std::forward */
|
||||
|
||||
template <typename T>
|
||||
hb_remove_reference (T)&& hb_move (T&& t) { return (hb_remove_reference (T)&&) (t); }
|
||||
|
||||
template <typename T>
|
||||
T&& hb_forward (hb_remove_reference (T)& t) { return (T&&) t; }
|
||||
template <typename T>
|
||||
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; };
|
||||
|
||||
|
|
|
@ -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<link_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 <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)
|
||||
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)
|
||||
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<Type> ();
|
||||
assert (!current.length);
|
||||
return push<Type> ();
|
||||
}
|
||||
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 <typename Type>
|
||||
Type *push ()
|
||||
{
|
||||
object_t obj;
|
||||
obj.head = head;
|
||||
obj.tail = tail;
|
||||
current.push (obj);
|
||||
return start_embed<Type> ();
|
||||
}
|
||||
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 <typename Type>
|
||||
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 <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);
|
||||
/* 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 <typename Type>
|
||||
Type *copy () const
|
||||
{ return reinterpret_cast<Type *> ((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<object_t> current;
|
||||
|
||||
/* Stack of packed objects. Object 0 is always nil object. */
|
||||
hb_vector_t<object_t> packed;
|
||||
|
||||
/* Map view of packed objects. */
|
||||
hb_hashmap_t<const object_t *, objidx_t, nullptr, 0> packed_map;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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 ();
|
||||
|
|
|
@ -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 <typename T>
|
||||
hb_vector_t& operator << (const T& v) { push (v); return *this; }
|
||||
hb_vector_t& operator << (T&& v) { push (hb_forward<T> (v)); return *this; }
|
||||
|
||||
hb_array_t< Type> as_array () { return hb_array (arrayZ(), length); }
|
||||
hb_array_t<const Type> as_array () const { return hb_array (arrayZ(), length); }
|
||||
|
@ -131,10 +171,10 @@ struct hb_vector_t
|
|||
return &arrayZ()[length - 1];
|
||||
}
|
||||
template <typename T>
|
||||
Type *push (const T& v)
|
||||
Type *push (T&& v)
|
||||
{
|
||||
Type *p = push ();
|
||||
*p = v;
|
||||
*p = hb_forward<T> (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)
|
||||
|
|
Loading…
Reference in New Issue