Imported Upstream version 8.2.2
[platform/upstream/harfbuzz.git] / src / hb-serialize.hh
index 6615f03..15eccb6 100644 (file)
@@ -36,6 +36,9 @@
 #include "hb-map.hh"
 #include "hb-pool.hh"
 
+#ifdef HB_EXPERIMENTAL_API
+#include "hb-subset-repacker.h"
+#endif
 
 /*
  * Serialize
@@ -70,6 +73,33 @@ struct hb_serialize_context_t
       virtual_links.fini ();
     }
 
+    object_t () = default;
+
+#ifdef HB_EXPERIMENTAL_API
+    object_t (const hb_object_t &o)
+    {
+      head = o.head;
+      tail = o.tail;
+      next = nullptr;
+      real_links.alloc (o.num_real_links, true);
+      for (unsigned i = 0 ; i < o.num_real_links; i++)
+        real_links.push (o.real_links[i]);
+
+      virtual_links.alloc (o.num_virtual_links, true);
+      for (unsigned i = 0; i < o.num_virtual_links; i++)
+        virtual_links.push (o.virtual_links[i]);
+    }
+#endif
+
+    friend void swap (object_t& a, object_t& b)
+    {
+      hb_swap (a.head, b.head);
+      hb_swap (a.tail, b.tail);
+      hb_swap (a.next, b.next);
+      hb_swap (a.real_links, b.real_links);
+      hb_swap (a.virtual_links, b.virtual_links);
+    }
+
     bool operator == (const object_t &o) const
     {
       // Virtual links aren't considered for equality since they don't affect the functionality
@@ -83,18 +113,40 @@ struct hb_serialize_context_t
     {
       // 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 () ^
+      return hb_bytes_t (head, hb_min (128, tail - head)).hash () ^
           real_links.as_bytes ().hash ();
     }
 
     struct link_t
     {
       unsigned width: 3;
-      bool is_signed: 1;
+      unsigned is_signed: 1;
       unsigned whence: 2;
-      unsigned position: 28;
-      unsigned bias;
+      unsigned bias : 26;
+      unsigned position;
       objidx_t objidx;
+
+      link_t () = default;
+
+#ifdef HB_EXPERIMENTAL_API
+      link_t (const hb_link_t &o)
+      {
+        width = o.width;
+        is_signed = 0;
+        whence = 0;
+        position = o.position;
+        bias = 0;
+        objidx = o.objidx;
+      }
+#endif
+
+      HB_INTERNAL static int cmp (const void* a, const void* b)
+      {
+        int cmp = ((const link_t*)a)->position - ((const link_t*)b)->position;
+        if (cmp) return cmp;
+
+        return ((const link_t*)a)->objidx - ((const link_t*)b)->objidx;
+      }
     };
 
     char *head;
@@ -120,8 +172,14 @@ struct hb_serialize_context_t
   };
 
   snapshot_t snapshot ()
-  { return snapshot_t {
-      head, tail, current, current->real_links.length, current->virtual_links.length, errors }; }
+  {
+    return snapshot_t {
+      head, tail, current,
+      current ? current->real_links.length : 0,
+      current ? current->virtual_links.length : 0,
+      errors
+     };
+  }
 
   hb_serialize_context_t (void *start_, unsigned int size) :
     start ((char *) start_),
@@ -142,7 +200,6 @@ struct hb_serialize_context_t
       current = current->next;
       _->fini ();
     }
-    object_pool.fini ();
   }
 
   bool in_error () const { return bool (errors); }
@@ -172,6 +229,7 @@ struct hb_serialize_context_t
     this->errors = HB_SERIALIZE_ERROR_NONE;
     this->head = this->start;
     this->tail = this->end;
+    this->zerocopy = nullptr;
     this->debug_depth = 0;
 
     fini ();
@@ -208,7 +266,8 @@ struct hb_serialize_context_t
           propagate_error (std::forward<Ts> (os)...); }
 
   /* To be called around main operation. */
-  template <typename Type>
+  template <typename Type=char>
+  __attribute__((returns_nonnull))
   Type *start_serialize ()
   {
     DEBUG_MSG_LEVEL (SERIALIZE, this->start, 0, +1,
@@ -251,6 +310,7 @@ struct hb_serialize_context_t
   }
 
   template <typename Type = void>
+  __attribute__((returns_nonnull))
   Type *push ()
   {
     if (unlikely (in_error ())) return start_embed<Type> ();
@@ -271,10 +331,13 @@ struct hb_serialize_context_t
   {
     object_t *obj = current;
     if (unlikely (!obj)) return;
-    if (unlikely (in_error())) return;
+    // Allow cleanup when we've error'd out on int overflows which don't compromise
+    // the serializer state.
+    if (unlikely (in_error() && !only_overflow ())) return;
 
     current = current->next;
-    revert (obj->head, obj->tail);
+    revert (zerocopy ? zerocopy : obj->head, obj->tail);
+    zerocopy = nullptr;
     obj->fini ();
     object_pool.release (obj);
   }
@@ -287,13 +350,18 @@ struct hb_serialize_context_t
   {
     object_t *obj = current;
     if (unlikely (!obj)) return 0;
-    if (unlikely (in_error())) return 0;
+    // Allow cleanup when we've error'd out on int overflows which don't compromise
+    // the serializer state.
+    if (unlikely (in_error()  && !only_overflow ())) return 0;
 
     current = current->next;
     obj->tail = head;
     obj->next = nullptr;
+    assert (obj->head <= obj->tail);
     unsigned len = obj->tail - obj->head;
-    head = obj->head; /* Rewind head. */
+    head = zerocopy ? zerocopy : obj->head; /* Rewind head. */
+    bool was_zerocopy = zerocopy;
+    zerocopy = nullptr;
 
     if (!len)
     {
@@ -303,9 +371,11 @@ struct hb_serialize_context_t
     }
 
     objidx_t objidx;
+    uint32_t hash = 0;
     if (share)
     {
-      objidx = packed_map.get (obj);
+      hash = hb_hash (obj);
+      objidx = packed_map.get_with_hash (obj, hash);
       if (objidx)
       {
         merge_virtual_links (obj, objidx);
@@ -315,7 +385,10 @@ struct hb_serialize_context_t
     }
 
     tail -= len;
-    memmove (tail, obj->head, len);
+    if (was_zerocopy)
+      assert (tail == obj->head);
+    else
+      memmove (tail, obj->head, len);
 
     obj->head = tail;
     obj->tail = tail + len;
@@ -333,7 +406,7 @@ struct hb_serialize_context_t
 
     objidx = packed.length - 1;
 
-    if (share) packed_map.set (obj, objidx);
+    if (share) packed_map.set_with_hash (obj, hash, objidx);
     propagate_error (packed_map);
 
     return objidx;
@@ -344,8 +417,11 @@ struct hb_serialize_context_t
     // Overflows that happened after the snapshot will be erased by the revert.
     if (unlikely (in_error () && !only_overflow ())) return;
     assert (snap.current == current);
-    current->real_links.shrink (snap.num_real_links);
-    current->virtual_links.shrink (snap.num_virtual_links);
+    if (current)
+    {
+      current->real_links.shrink (snap.num_real_links);
+      current->virtual_links.shrink (snap.num_virtual_links);
+    }
     errors = snap.errors;
     revert (snap.head, snap.tail);
   }
@@ -502,13 +578,15 @@ struct hb_serialize_context_t
   {
     unsigned int l = length () % alignment;
     if (l)
-      allocate_size<void> (alignment - l);
+      (void) allocate_size<void> (alignment - l);
   }
 
   template <typename Type = void>
+  __attribute__((returns_nonnull))
   Type *start_embed (const Type *obj HB_UNUSED = nullptr) const
   { return reinterpret_cast<Type *> (this->head); }
   template <typename Type>
+  __attribute__((returns_nonnull))
   Type *start_embed (const Type &obj) const
   { return start_embed (std::addressof (obj)); }
 
@@ -517,8 +595,27 @@ struct hb_serialize_context_t
     return !bool ((errors = (errors | err_type)));
   }
 
+  bool start_zerocopy (size_t size)
+  {
+    if (unlikely (in_error ())) return false;
+
+    if (unlikely (size > INT_MAX || this->tail - this->head < ptrdiff_t (size)))
+    {
+      err (HB_SERIALIZE_ERROR_OUT_OF_ROOM);
+      return false;
+    }
+
+    assert (!this->zerocopy);
+    this->zerocopy = this->head;
+
+    assert (this->current->head == this->head);
+    this->current->head = this->current->tail = this->head = this->tail - size;
+    return true;
+  }
+
   template <typename Type>
-  Type *allocate_size (size_t size)
+  HB_NODISCARD
+  Type *allocate_size (size_t size, bool clear = true)
   {
     if (unlikely (in_error ())) return nullptr;
 
@@ -527,7 +624,8 @@ struct hb_serialize_context_t
       err (HB_SERIALIZE_ERROR_OUT_OF_ROOM);
       return nullptr;
     }
-    hb_memset (this->head, 0, size);
+    if (clear)
+      hb_memset (this->head, 0, size);
     char *ret = this->head;
     this->head += size;
     return reinterpret_cast<Type *> (ret);
@@ -538,17 +636,26 @@ struct hb_serialize_context_t
   { return this->allocate_size<Type> (Type::min_size); }
 
   template <typename Type>
+  HB_NODISCARD
   Type *embed (const Type *obj)
   {
     unsigned int size = obj->get_size ();
-    Type *ret = this->allocate_size<Type> (size);
+    Type *ret = this->allocate_size<Type> (size, false);
     if (unlikely (!ret)) return nullptr;
-    memcpy (ret, obj, size);
+    hb_memcpy (ret, obj, size);
     return ret;
   }
   template <typename Type>
+  HB_NODISCARD
   Type *embed (const Type &obj)
   { return embed (std::addressof (obj)); }
+  char *embed (const char *obj, unsigned size)
+  {
+    char *ret = this->allocate_size<char> (size, false);
+    if (unlikely (!ret)) return nullptr;
+    hb_memcpy (ret, obj, size);
+    return ret;
+  }
 
   template <typename Type, typename ...Ts> auto
   _copy (const Type &src, hb_priority<1>, Ts&&... ds) HB_RETURN
@@ -564,7 +671,7 @@ struct hb_serialize_context_t
   }
 
   /* Like embed, but active: calls obj.operator=() or obj.copy() to transfer data
-   * instead of memcpy(). */
+   * instead of hb_memcpy(). */
   template <typename Type, typename ...Ts>
   Type *copy (const Type &src, Ts&&... ds)
   { return _copy (src, hb_prioritize, std::forward<Ts> (ds)...); }
@@ -582,7 +689,7 @@ struct hb_serialize_context_t
   hb_serialize_context_t& operator << (const Type &obj) & { embed (obj); return *this; }
 
   template <typename Type>
-  Type *extend_size (Type *obj, size_t size)
+  Type *extend_size (Type *obj, size_t size, bool clear = true)
   {
     if (unlikely (in_error ())) return nullptr;
 
@@ -590,12 +697,12 @@ struct hb_serialize_context_t
     assert ((char *) obj <= this->head);
     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;
+                 !this->allocate_size<Type> (((char *) obj) + size - this->head, clear))) return nullptr;
     return reinterpret_cast<Type *> (obj);
   }
   template <typename Type>
-  Type *extend_size (Type &obj, size_t size)
-  { return extend_size (std::addressof (obj), size); }
+  Type *extend_size (Type &obj, size_t size, bool clear = true)
+  { return extend_size (std::addressof (obj), size, clear); }
 
   template <typename Type>
   Type *extend_min (Type *obj) { return extend_size (obj, obj->min_size); }
@@ -624,8 +731,8 @@ struct hb_serialize_context_t
     char *p = (char *) hb_malloc (len);
     if (unlikely (!p)) return hb_bytes_t ();
 
-    memcpy (p, this->start, this->head - this->start);
-    memcpy (p + (this->head - this->start), this->tail, this->end - this->tail);
+    hb_memcpy (p, this->start, this->head - this->start);
+    hb_memcpy (p + (this->head - this->start), this->tail, this->end - this->tail);
     return hb_bytes_t (p, len);
   }
   template <typename Type>
@@ -651,8 +758,8 @@ struct hb_serialize_context_t
     check_assign (off, offset, HB_SERIALIZE_ERROR_OFFSET_OVERFLOW);
   }
 
-  public: /* TODO Make private. */
-  char *start, *head, *tail, *end;
+  public:
+  char *start, *head, *tail, *end, *zerocopy;
   unsigned int debug_depth;
   hb_serialize_error_t errors;
 
@@ -675,9 +782,7 @@ struct hb_serialize_context_t
   hb_vector_t<object_t *> packed;
 
   /* Map view of packed objects. */
-  hb_hashmap_t<const object_t *, objidx_t,
-              const object_t *, objidx_t,
-              nullptr, 0> packed_map;
+  hb_hashmap_t<const object_t *, objidx_t> packed_map;
 };
 
 #endif /* HB_SERIALIZE_HH */