Imported Upstream version 3.4.0
[platform/upstream/harfbuzz.git] / src / hb-serialize.hh
index 4fb4f4e..6615f03 100644 (file)
  * Serialize
  */
 
+enum hb_serialize_error_t {
+  HB_SERIALIZE_ERROR_NONE =            0x00000000u,
+  HB_SERIALIZE_ERROR_OTHER =           0x00000001u,
+  HB_SERIALIZE_ERROR_OFFSET_OVERFLOW = 0x00000002u,
+  HB_SERIALIZE_ERROR_OUT_OF_ROOM =     0x00000004u,
+  HB_SERIALIZE_ERROR_INT_OVERFLOW =    0x00000008u,
+  HB_SERIALIZE_ERROR_ARRAY_OVERFLOW =  0x00000010u
+};
+HB_MARK_AS_FLAG_T (hb_serialize_error_t);
+
 struct hb_serialize_context_t
 {
   typedef unsigned objidx_t;
@@ -51,26 +61,35 @@ struct hb_serialize_context_t
      Absolute  /* Absolute: from the start of the serialize buffer. */
    };
 
+
+
   struct object_t
   {
-    void fini () { links.fini (); }
+    void fini () {
+      real_links.fini ();
+      virtual_links.fini ();
+    }
 
     bool operator == (const object_t &o) const
     {
+      // Virtual links aren't considered for equality since they don't affect the functionality
+      // of the object.
       return (tail - head == o.tail - o.head)
-         && (links.length == o.links.length)
+         && (real_links.length == o.real_links.length)
          && 0 == hb_memcmp (head, o.head, tail - head)
-         && links.as_bytes () == o.links.as_bytes ();
+         && real_links.as_bytes () == o.real_links.as_bytes ();
     }
     uint32_t hash () const
     {
+      // Virtual links aren't considered for equality since they don't affect the functionality
+      // of the object.
       return hb_bytes_t (head, tail - head).hash () ^
-            links.as_bytes ().hash ();
+          real_links.as_bytes ().hash ();
     }
 
     struct link_t
     {
-      bool is_wide: 1;
+      unsigned width: 3;
       bool is_signed: 1;
       unsigned whence: 2;
       unsigned position: 28;
@@ -80,8 +99,14 @@ struct hb_serialize_context_t
 
     char *head;
     char *tail;
-    hb_vector_t<link_t> links;
+    hb_vector_t<link_t> real_links;
+    hb_vector_t<link_t> virtual_links;
     object_t *next;
+
+    auto all_links () const HB_AUTO_RETURN
+        (( hb_concat (this->real_links, this->virtual_links) ));
+    auto all_links_writer () HB_AUTO_RETURN
+        (( hb_concat (this->real_links.writer (), this->virtual_links.writer ()) ));
   };
 
   struct snapshot_t
@@ -89,11 +114,14 @@ struct hb_serialize_context_t
     char *head;
     char *tail;
     object_t *current; // Just for sanity check
-    unsigned num_links;
+    unsigned num_real_links;
+    unsigned num_virtual_links;
+    hb_serialize_error_t errors;
   };
 
   snapshot_t snapshot ()
-  { return snapshot_t { head, tail, current, current->links.length }; }
+  { return snapshot_t {
+      head, tail, current, current->real_links.length, current->virtual_links.length, errors }; }
 
   hb_serialize_context_t (void *start_, unsigned int size) :
     start ((char *) start_),
@@ -117,37 +145,67 @@ struct hb_serialize_context_t
     object_pool.fini ();
   }
 
-  bool in_error () const { return !this->successful; }
+  bool in_error () const { return bool (errors); }
+
+  bool successful () const { return !bool (errors); }
+
+  HB_NODISCARD bool ran_out_of_room () const { return errors & HB_SERIALIZE_ERROR_OUT_OF_ROOM; }
+  HB_NODISCARD bool offset_overflow () const { return errors & HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; }
+  HB_NODISCARD bool only_offset_overflow () const { return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW; }
+  HB_NODISCARD bool only_overflow () const
+  {
+    return errors == HB_SERIALIZE_ERROR_OFFSET_OVERFLOW
+        || errors == HB_SERIALIZE_ERROR_INT_OVERFLOW
+        || errors == HB_SERIALIZE_ERROR_ARRAY_OVERFLOW;
+  }
+
+  void reset (void *start_, unsigned int size)
+  {
+    start = (char*) start_;
+    end = start + size;
+    reset ();
+    current = nullptr;
+  }
 
   void reset ()
   {
-    this->successful = true;
-    this->ran_out_of_room = false;
+    this->errors = HB_SERIALIZE_ERROR_NONE;
     this->head = this->start;
     this->tail = this->end;
     this->debug_depth = 0;
 
     fini ();
     this->packed.push (nullptr);
+    this->packed_map.init ();
   }
 
-  bool check_success (bool success)
-  { return this->successful && (success || (err_other_error (), false)); }
+  bool check_success (bool success,
+                      hb_serialize_error_t err_type = HB_SERIALIZE_ERROR_OTHER)
+  {
+    return successful ()
+        && (success || err (err_type));
+  }
 
   template <typename T1, typename T2>
-  bool check_equal (T1 &&v1, T2 &&v2)
-  { return check_success ((long long) v1 == (long long) v2); }
+  bool check_equal (T1 &&v1, T2 &&v2, hb_serialize_error_t err_type)
+  {
+    if ((long long) v1 != (long long) v2)
+    {
+      return err (err_type);
+    }
+    return true;
+  }
 
   template <typename T1, typename T2>
-  bool check_assign (T1 &v1, T2 &&v2)
-  { return check_equal (v1 = v2, v2); }
+  bool check_assign (T1 &v1, T2 &&v2, hb_serialize_error_t err_type)
+  { return check_equal (v1 = v2, v2, err_type); }
 
   template <typename T> bool propagate_error (T &&obj)
   { return check_success (!hb_deref (obj).in_error ()); }
 
   template <typename T1, typename... Ts> bool propagate_error (T1 &&o1, Ts&&... os)
-  { return propagate_error (hb_forward<T1> (o1)) &&
-          propagate_error (hb_forward<Ts> (os)...); }
+  { return propagate_error (std::forward<T1> (o1)) &&
+          propagate_error (std::forward<Ts> (os)...); }
 
   /* To be called around main operation. */
   template <typename Type>
@@ -167,11 +225,19 @@ struct hb_serialize_context_t
                     "end [%p..%p] serialized %u bytes; %s",
                     this->start, this->end,
                     (unsigned) (this->head - this->start),
-                    this->successful ? "successful" : "UNSUCCESSFUL");
+                    successful () ? "successful" : "UNSUCCESSFUL");
 
     propagate_error (packed, packed_map);
 
     if (unlikely (!current)) return;
+    if (unlikely (in_error()))
+    {
+      // Offset overflows that occur before link resolution cannot be handled
+      // by repacking, so set a more general error.
+      if (offset_overflow ()) err (HB_SERIALIZE_ERROR_OTHER);
+      return;
+    }
+
     assert (!current->next);
 
     /* Only "pack" if there exist other objects... Otherwise, don't bother.
@@ -187,6 +253,8 @@ struct hb_serialize_context_t
   template <typename Type = void>
   Type *push ()
   {
+    if (unlikely (in_error ())) return start_embed<Type> ();
+
     object_t *obj = object_pool.alloc ();
     if (unlikely (!obj))
       check_success (false);
@@ -203,13 +271,15 @@ struct hb_serialize_context_t
   {
     object_t *obj = current;
     if (unlikely (!obj)) return;
+    if (unlikely (in_error())) return;
+
     current = current->next;
     revert (obj->head, obj->tail);
     obj->fini ();
-    object_pool.free (obj);
+    object_pool.release (obj);
   }
 
-  /* Set share to false when an object is unlikely sharable with others
+  /* Set share to false when an object is unlikely shareable with others
    * so not worth an attempt, or a contiguous table is serialized as
    * multiple consecutive objects in the reverse order so can't be shared.
    */
@@ -217,6 +287,8 @@ struct hb_serialize_context_t
   {
     object_t *obj = current;
     if (unlikely (!obj)) return 0;
+    if (unlikely (in_error())) return 0;
+
     current = current->next;
     obj->tail = head;
     obj->next = nullptr;
@@ -225,7 +297,8 @@ struct hb_serialize_context_t
 
     if (!len)
     {
-      assert (!obj->links.length);
+      assert (!obj->real_links.length);
+      assert (!obj->virtual_links.length);
       return 0;
     }
 
@@ -235,6 +308,7 @@ struct hb_serialize_context_t
       objidx = packed_map.get (obj);
       if (objidx)
       {
+        merge_virtual_links (obj, objidx);
        obj->fini ();
        return objidx;
       }
@@ -248,25 +322,38 @@ struct hb_serialize_context_t
 
     packed.push (obj);
 
-    if (unlikely (packed.in_error ()))
+    if (unlikely (!propagate_error (packed)))
+    {
+      /* Obj wasn't successfully added to packed, so clean it up otherwise its
+       * links will be leaked. When we use constructor/destructors properly, we
+       * can remove these. */
+      obj->fini ();
       return 0;
+    }
 
     objidx = packed.length - 1;
 
     if (share) packed_map.set (obj, objidx);
+    propagate_error (packed_map);
 
     return objidx;
   }
 
   void revert (snapshot_t snap)
   {
+    // Overflows that happened after the snapshot will be erased by the revert.
+    if (unlikely (in_error () && !only_overflow ())) return;
     assert (snap.current == current);
-    current->links.shrink (snap.num_links);
+    current->real_links.shrink (snap.num_real_links);
+    current->virtual_links.shrink (snap.num_virtual_links);
+    errors = snap.errors;
     revert (snap.head, snap.tail);
   }
+
   void revert (char *snap_head,
               char *snap_tail)
   {
+    if (unlikely (in_error ())) return;
     assert (snap_head <= head);
     assert (tail <= snap_tail);
     head = snap_head;
@@ -276,6 +363,7 @@ struct hb_serialize_context_t
 
   void discard_stale_objects ()
   {
+    if (unlikely (in_error ())) return;
     while (packed.length > 1 &&
           packed.tail ()->head < tail)
     {
@@ -288,12 +376,41 @@ struct hb_serialize_context_t
       assert (packed.tail ()->head == tail);
   }
 
+  // Adds a virtual link from the current object to objidx. A virtual link is not associated with
+  // an actual offset field. They are solely used to enforce ordering constraints between objects.
+  // Adding a virtual link from object a to object b will ensure that object b is always packed after
+  // object a in the final serialized order.
+  //
+  // This is useful in certain situations where there needs to be a specific ordering in the
+  // final serialization. Such as when platform bugs require certain orderings, or to provide
+  //  guidance to the repacker for better offset overflow resolution.
+  void add_virtual_link (objidx_t objidx)
+  {
+    if (unlikely (in_error ())) return;
+
+    if (!objidx)
+      return;
+
+    assert (current);
+
+    auto& link = *current->virtual_links.push ();
+    if (current->virtual_links.in_error ())
+      err (HB_SERIALIZE_ERROR_OTHER);
+
+    link.width = 0;
+    link.objidx = objidx;
+    link.is_signed = 0;
+    link.whence = 0;
+    link.position = 0;
+    link.bias = 0;
+  }
+
   template <typename T>
   void add_link (T &ofs, objidx_t objidx,
                 whence_t whence = Head,
                 unsigned bias = 0)
   {
-    static_assert (sizeof (T) == 2 || sizeof (T) == 4, "");
+    if (unlikely (in_error ())) return;
 
     if (!objidx)
       return;
@@ -301,18 +418,32 @@ struct hb_serialize_context_t
     assert (current);
     assert (current->head <= (const char *) &ofs);
 
-    auto& link = *current->links.push ();
+    auto& link = *current->real_links.push ();
+    if (current->real_links.in_error ())
+      err (HB_SERIALIZE_ERROR_OTHER);
 
-    link.is_wide = sizeof (T) == 4;
-    link.is_signed = hb_is_signed (hb_unwrap_type (T));
+    link.width = sizeof (T);
+    link.objidx = objidx;
+    if (unlikely (!sizeof (T)))
+    {
+      // This link is not associated with an actual offset and exists merely to enforce
+      // an ordering constraint.
+      link.is_signed = 0;
+      link.whence = 0;
+      link.position = 0;
+      link.bias = 0;
+      return;
+    }
+
+    link.is_signed = std::is_signed<hb_unwrap_type (T)>::value;
     link.whence = (unsigned) whence;
     link.position = (const char *) &ofs - current->head;
     link.bias = bias;
-    link.objidx = objidx;
   }
 
   unsigned to_bias (const void *base) const
   {
+    if (unlikely (in_error ())) return 0;
     if (!base) return 0;
     assert (current);
     assert (current->head <= (const char *) base);
@@ -327,10 +458,10 @@ struct hb_serialize_context_t
     assert (packed.length > 1);
 
     for (const object_t* parent : ++hb_iter (packed))
-      for (const object_t::link_t &link : parent->links)
+      for (const object_t::link_t &link : parent->real_links)
       {
        const object_t* child = packed[link.objidx];
-       if (unlikely (!child)) { err_other_error(); return; }
+       if (unlikely (!child)) { err (HB_SERIALIZE_ERROR_OTHER); return; }
        unsigned offset = 0;
        switch ((whence_t) link.whence) {
        case Head:     offset = child->head - parent->head; break;
@@ -342,22 +473,30 @@ struct hb_serialize_context_t
        offset -= link.bias;
        if (link.is_signed)
        {
-         if (link.is_wide)
+         assert (link.width == 2 || link.width == 4);
+         if (link.width == 4)
            assign_offset<int32_t> (parent, link, offset);
          else
            assign_offset<int16_t> (parent, link, offset);
        }
        else
        {
-         if (link.is_wide)
+         assert (link.width == 2 || link.width == 3 || link.width == 4);
+         if (link.width == 4)
            assign_offset<uint32_t> (parent, link, offset);
+         else if (link.width == 3)
+           assign_offset<uint32_t, 3> (parent, link, offset);
          else
            assign_offset<uint16_t> (parent, link, offset);
        }
       }
   }
 
-  unsigned int length () const { return this->head - current->head; }
+  unsigned int length () const
+  {
+    if (unlikely (!current)) return 0;
+    return this->head - current->head;
+  }
 
   void align (unsigned int alignment)
   {
@@ -371,24 +510,24 @@ struct hb_serialize_context_t
   { return reinterpret_cast<Type *> (this->head); }
   template <typename Type>
   Type *start_embed (const Type &obj) const
-  { return start_embed (hb_addressof (obj)); }
+  { return start_embed (std::addressof (obj)); }
 
-  /* Following two functions exist to allow setting breakpoint on. */
-  void err_ran_out_of_room () { this->ran_out_of_room = true; }
-  void err_other_error () { this->successful = false; }
+  bool err (hb_serialize_error_t err_type)
+  {
+    return !bool ((errors = (errors | err_type)));
+  }
 
   template <typename Type>
-  Type *allocate_size (unsigned int size)
+  Type *allocate_size (size_t size)
   {
-    if (unlikely (!this->successful)) return nullptr;
+    if (unlikely (in_error ())) return nullptr;
 
-    if (this->tail - this->head < ptrdiff_t (size))
+    if (unlikely (size > INT_MAX || this->tail - this->head < ptrdiff_t (size)))
     {
-      err_ran_out_of_room ();
-      this->successful = false;
+      err (HB_SERIALIZE_ERROR_OUT_OF_ROOM);
       return nullptr;
     }
-    memset (this->head, 0, size);
+    hb_memset (this->head, 0, size);
     char *ret = this->head;
     this->head += size;
     return reinterpret_cast<Type *> (ret);
@@ -409,11 +548,11 @@ struct hb_serialize_context_t
   }
   template <typename Type>
   Type *embed (const Type &obj)
-  { return embed (hb_addressof (obj)); }
+  { return embed (std::addressof (obj)); }
 
   template <typename Type, typename ...Ts> auto
   _copy (const Type &src, hb_priority<1>, Ts&&... ds) HB_RETURN
-  (Type *, src.copy (this, hb_forward<Ts> (ds)...))
+  (Type *, src.copy (this, std::forward<Ts> (ds)...))
 
   template <typename Type> auto
   _copy (const Type &src, hb_priority<0>) -> decltype (&(hb_declval<Type> () = src))
@@ -428,54 +567,61 @@ struct hb_serialize_context_t
    * instead of memcpy(). */
   template <typename Type, typename ...Ts>
   Type *copy (const Type &src, Ts&&... ds)
-  { return _copy (src, hb_prioritize, hb_forward<Ts> (ds)...); }
+  { return _copy (src, hb_prioritize, std::forward<Ts> (ds)...); }
   template <typename Type, typename ...Ts>
   Type *copy (const Type *src, Ts&&... ds)
-  { return copy (*src, hb_forward<Ts> (ds)...); }
+  { return copy (*src, std::forward<Ts> (ds)...); }
 
   template<typename Iterator,
           hb_requires (hb_is_iterator (Iterator)),
           typename ...Ts>
   void copy_all (Iterator it, Ts&&... ds)
-  { for (decltype (*it) _ : it) copy (_, hb_forward<Ts> (ds)...); }
+  { for (decltype (*it) _ : it) copy (_, std::forward<Ts> (ds)...); }
 
   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)
+  Type *extend_size (Type *obj, size_t size)
   {
+    if (unlikely (in_error ())) return nullptr;
+
     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;
+    assert ((size_t) (this->head - (char *) obj) <= size);
+    if (unlikely (((char *) obj + size < (char *) obj) ||
+                 !this->allocate_size<Type> (((char *) obj) + size - this->head))) return nullptr;
     return reinterpret_cast<Type *> (obj);
   }
   template <typename Type>
-  Type *extend_size (Type &obj, unsigned int size)
-  { return extend_size (hb_addressof (obj), size); }
+  Type *extend_size (Type &obj, size_t size)
+  { return extend_size (std::addressof (obj), size); }
 
   template <typename Type>
   Type *extend_min (Type *obj) { return extend_size (obj, obj->min_size); }
   template <typename Type>
-  Type *extend_min (Type &obj) { return extend_min (hb_addressof (obj)); }
+  Type *extend_min (Type &obj) { return extend_min (std::addressof (obj)); }
 
   template <typename Type, typename ...Ts>
   Type *extend (Type *obj, Ts&&... ds)
-  { return extend_size (obj, obj->get_size (hb_forward<Ts> (ds)...)); }
+  { return extend_size (obj, obj->get_size (std::forward<Ts> (ds)...)); }
   template <typename Type, typename ...Ts>
   Type *extend (Type &obj, Ts&&... ds)
-  { return extend (hb_addressof (obj), hb_forward<Ts> (ds)...); }
+  { return extend (std::addressof (obj), std::forward<Ts> (ds)...); }
 
   /* Output routines. */
   hb_bytes_t copy_bytes () const
   {
-    assert (this->successful);
+    assert (successful ());
     /* 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 len is zero don't hb_malloc as the memory won't get properly
+    // cleaned up later.
+    if (!len) return hb_bytes_t ();
+
+    char *p = (char *) hb_malloc (len);
     if (unlikely (!p)) return hb_bytes_t ();
 
     memcpy (p, this->start, this->head - this->start);
@@ -490,26 +636,35 @@ struct hb_serialize_context_t
     hb_bytes_t b = copy_bytes ();
     return hb_blob_create (b.arrayZ, b.length,
                           HB_MEMORY_MODE_WRITABLE,
-                          (char *) b.arrayZ, free);
+                          (char *) b.arrayZ, hb_free);
   }
 
+  const hb_vector_t<object_t *>& object_graph() const
+  { return packed; }
+
   private:
-  template <typename T>
+  template <typename T, unsigned Size = sizeof (T)>
   void assign_offset (const object_t* parent, const object_t::link_t &link, unsigned offset)
   {
-    auto &off = * ((BEInt<T, sizeof (T)> *) (parent->head + link.position));
+    auto &off = * ((BEInt<T, Size> *) (parent->head + link.position));
     assert (0 == off);
-    check_assign (off, offset);
+    check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW);
   }
 
   public: /* TODO Make private. */
   char *start, *head, *tail, *end;
   unsigned int debug_depth;
-  bool successful;
-  bool ran_out_of_room;
+  hb_serialize_error_t errors;
 
   private:
 
+  void merge_virtual_links (const object_t* from, objidx_t to_idx) {
+    object_t* to = packed[to_idx];
+    for (const auto& l : from->virtual_links) {
+      to->virtual_links.push (l);
+    }
+  }
+
   /* Object memory pool. */
   hb_pool_t<object_t> object_pool;
 
@@ -520,8 +675,9 @@ struct hb_serialize_context_t
   hb_vector_t<object_t *> packed;
 
   /* Map view of packed objects. */
-  hb_hashmap_t<const object_t *, objidx_t, nullptr, 0> packed_map;
+  hb_hashmap_t<const object_t *, objidx_t,
+              const object_t *, objidx_t,
+              nullptr, 0> packed_map;
 };
 
-
 #endif /* HB_SERIALIZE_HH */