• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright © 2017,2018  Google, Inc.
3  *
4  *  This is part of HarfBuzz, a text shaping library.
5  *
6  * Permission is hereby granted, without written agreement and without
7  * license or royalty fees, to use, copy, modify, and distribute this
8  * software and its documentation for any purpose, provided that the
9  * above copyright notice and the following two paragraphs appear in
10  * all copies of this software.
11  *
12  * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
13  * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
14  * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
15  * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
16  * DAMAGE.
17  *
18  * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
19  * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
20  * FITNESS FOR A PARTICULAR PURPOSE.  THE SOFTWARE PROVIDED HEREUNDER IS
21  * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
22  * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
23  *
24  * Google Author(s): Behdad Esfahbod
25  */
26 
27 #ifndef HB_VECTOR_HH
28 #define HB_VECTOR_HH
29 
30 #include "hb.hh"
31 #include "hb-array.hh"
32 #include "hb-meta.hh"
33 #include "hb-null.hh"
34 
35 
36 template <typename Type,
37 	  bool sorted=false>
38 struct hb_vector_t
39 {
40   typedef Type item_t;
41   static constexpr unsigned item_size = hb_static_size (Type);
42   using array_t = typename std::conditional<sorted, hb_sorted_array_t<Type>, hb_array_t<Type>>::type;
43   using c_array_t = typename std::conditional<sorted, hb_sorted_array_t<const Type>, hb_array_t<const Type>>::type;
44 
45   hb_vector_t () = default;
hb_vector_thb_vector_t46   hb_vector_t (std::initializer_list<Type> lst) : hb_vector_t ()
47   {
48     alloc (lst.size (), true);
49     for (auto&& item : lst)
50       push (item);
51   }
52   template <typename Iterable,
53 	    hb_requires (hb_is_iterable (Iterable))>
hb_vector_thb_vector_t54   hb_vector_t (const Iterable &o) : hb_vector_t ()
55   {
56     auto iter = hb_iter (o);
57     if (iter.is_random_access_iterator || iter.has_fast_len)
58       alloc (hb_len (iter), true);
59     hb_copy (iter, *this);
60   }
hb_vector_thb_vector_t61   hb_vector_t (const hb_vector_t &o) : hb_vector_t ()
62   {
63     alloc (o.length, true);
64     if (unlikely (in_error ())) return;
65     copy_array (o.as_array ());
66   }
hb_vector_thb_vector_t67   hb_vector_t (array_t o) : hb_vector_t ()
68   {
69     alloc (o.length, true);
70     if (unlikely (in_error ())) return;
71     copy_array (o);
72   }
hb_vector_thb_vector_t73   hb_vector_t (c_array_t o) : hb_vector_t ()
74   {
75     alloc (o.length, true);
76     if (unlikely (in_error ())) return;
77     copy_array (o);
78   }
hb_vector_thb_vector_t79   hb_vector_t (hb_vector_t &&o)
80   {
81     allocated = o.allocated;
82     length = o.length;
83     arrayZ = o.arrayZ;
84     o.init ();
85   }
~hb_vector_thb_vector_t86   ~hb_vector_t () { fini (); }
87 
88   public:
89   int allocated = 0; /* < 0 means allocation failed. */
90   unsigned int length = 0;
91   public:
92   Type *arrayZ = nullptr;
93 
inithb_vector_t94   void init ()
95   {
96     allocated = length = 0;
97     arrayZ = nullptr;
98   }
init0hb_vector_t99   void init0 ()
100   {
101   }
102 
finihb_vector_t103   void fini ()
104   {
105     /* We allow a hack to make the vector point to a foreign array
106      * by the user. In that case length/arrayZ are non-zero but
107      * allocated is zero. Don't free anything. */
108     if (allocated)
109     {
110       shrink_vector (0);
111       hb_free (arrayZ);
112     }
113     init ();
114   }
115 
resethb_vector_t116   void reset ()
117   {
118     if (unlikely (in_error ()))
119       reset_error ();
120     resize (0);
121   }
122 
swap(hb_vector_t & a,hb_vector_t & b)123   friend void swap (hb_vector_t& a, hb_vector_t& b)
124   {
125     hb_swap (a.allocated, b.allocated);
126     hb_swap (a.length, b.length);
127     hb_swap (a.arrayZ, b.arrayZ);
128   }
129 
operator =hb_vector_t130   hb_vector_t& operator = (const hb_vector_t &o)
131   {
132     reset ();
133     alloc (o.length, true);
134     if (unlikely (in_error ())) return *this;
135 
136     copy_array (o.as_array ());
137 
138     return *this;
139   }
operator =hb_vector_t140   hb_vector_t& operator = (hb_vector_t &&o)
141   {
142     hb_swap (*this, o);
143     return *this;
144   }
145 
as_byteshb_vector_t146   hb_bytes_t as_bytes () const
147   { return hb_bytes_t ((const char *) arrayZ, get_size ()); }
148 
operator ==hb_vector_t149   bool operator == (const hb_vector_t &o) const { return as_array () == o.as_array (); }
operator !=hb_vector_t150   bool operator != (const hb_vector_t &o) const { return !(*this == o); }
hashhb_vector_t151   uint32_t hash () const { return as_array ().hash (); }
152 
operator []hb_vector_t153   Type& operator [] (int i_)
154   {
155     unsigned int i = (unsigned int) i_;
156     if (unlikely (i >= length))
157       return Crap (Type);
158     return arrayZ[i];
159   }
operator []hb_vector_t160   const Type& operator [] (int i_) const
161   {
162     unsigned int i = (unsigned int) i_;
163     if (unlikely (i >= length))
164       return Null (Type);
165     return arrayZ[i];
166   }
167 
tailhb_vector_t168   Type& tail () { return (*this)[length - 1]; }
tailhb_vector_t169   const Type& tail () const { return (*this)[length - 1]; }
170 
operator boolhb_vector_t171   explicit operator bool () const { return length; }
get_sizehb_vector_t172   unsigned get_size () const { return length * item_size; }
173 
174   /* Sink interface. */
175   template <typename T>
operator <<hb_vector_t176   hb_vector_t& operator << (T&& v) { push (std::forward<T> (v)); return *this; }
177 
as_arrayhb_vector_t178   array_t   as_array ()       { return hb_array (arrayZ, length); }
as_arrayhb_vector_t179   c_array_t as_array () const { return hb_array (arrayZ, length); }
180 
181   /* Iterator. */
182   typedef c_array_t   iter_t;
183   typedef array_t   writer_t;
iterhb_vector_t184     iter_t   iter () const { return as_array (); }
writerhb_vector_t185   writer_t writer ()       { return as_array (); }
operator iter_thb_vector_t186   operator   iter_t () const { return   iter (); }
operator writer_thb_vector_t187   operator writer_t ()       { return writer (); }
188 
189   /* Faster range-based for loop. */
beginhb_vector_t190   Type *begin () const { return arrayZ; }
endhb_vector_t191   Type *end () const { return arrayZ + length; }
192 
193 
as_sorted_arrayhb_vector_t194   hb_sorted_array_t<Type> as_sorted_array ()
195   { return hb_sorted_array (arrayZ, length); }
as_sorted_arrayhb_vector_t196   hb_sorted_array_t<const Type> as_sorted_array () const
197   { return hb_sorted_array (arrayZ, length); }
198 
operator T*hb_vector_t199   template <typename T> explicit operator T * () { return arrayZ; }
operator const T*hb_vector_t200   template <typename T> explicit operator const T * () const { return arrayZ; }
201 
operator +hb_vector_t202   Type * operator  + (unsigned int i) { return arrayZ + i; }
operator +hb_vector_t203   const Type * operator  + (unsigned int i) const { return arrayZ + i; }
204 
pushhb_vector_t205   Type *push ()
206   {
207     if (unlikely (!resize (length + 1)))
208       return std::addressof (Crap (Type));
209     return std::addressof (arrayZ[length - 1]);
210   }
pushhb_vector_t211   template <typename... Args> Type *push (Args&&... args)
212   {
213     if (unlikely ((int) length >= allocated && !alloc (length + 1)))
214       // If push failed to allocate then don't copy v, since this may cause
215       // the created copy to leak memory since we won't have stored a
216       // reference to it.
217       return std::addressof (Crap (Type));
218 
219     /* Emplace. */
220     Type *p = std::addressof (arrayZ[length++]);
221     return new (p) Type (std::forward<Args> (args)...);
222   }
223 
in_errorhb_vector_t224   bool in_error () const { return allocated < 0; }
set_errorhb_vector_t225   void set_error ()
226   {
227     assert (allocated >= 0);
228     allocated = -allocated - 1;
229   }
reset_errorhb_vector_t230   void reset_error ()
231   {
232     assert (allocated < 0);
233     allocated = -(allocated + 1);
234   }
235 
236   template <typename T = Type,
237 	    hb_enable_if (hb_is_trivially_copy_assignable(T))>
238   Type *
realloc_vectorhb_vector_t239   realloc_vector (unsigned new_allocated, hb_priority<0>)
240   {
241     if (!new_allocated)
242     {
243       hb_free (arrayZ);
244       return nullptr;
245     }
246     return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type));
247   }
248   template <typename T = Type,
249 	    hb_enable_if (!hb_is_trivially_copy_assignable(T))>
250   Type *
realloc_vectorhb_vector_t251   realloc_vector (unsigned new_allocated, hb_priority<0>)
252   {
253     if (!new_allocated)
254     {
255       hb_free (arrayZ);
256       return nullptr;
257     }
258     Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type));
259     if (likely (new_array))
260     {
261       for (unsigned i = 0; i < length; i++)
262       {
263 	new (std::addressof (new_array[i])) Type ();
264 	new_array[i] = std::move (arrayZ[i]);
265 	arrayZ[i].~Type ();
266       }
267       hb_free (arrayZ);
268     }
269     return new_array;
270   }
271   /* Specialization for hb_vector_t<hb_{vector,array}_t<U>> to speed up. */
272   template <typename T = Type,
273 	    hb_enable_if (hb_is_same (T, hb_vector_t<typename T::item_t>) ||
274 			  hb_is_same (T, hb_array_t <typename T::item_t>))>
275   Type *
realloc_vectorhb_vector_t276   realloc_vector (unsigned new_allocated, hb_priority<1>)
277   {
278     if (!new_allocated)
279     {
280       hb_free (arrayZ);
281       return nullptr;
282     }
283     return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type));
284   }
285 
286   template <typename T = Type,
287 	    hb_enable_if (hb_is_trivially_constructible(T))>
288   void
grow_vectorhb_vector_t289   grow_vector (unsigned size, hb_priority<0>)
290   {
291     hb_memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
292     length = size;
293   }
294   template <typename T = Type,
295 	    hb_enable_if (!hb_is_trivially_constructible(T))>
296   void
grow_vectorhb_vector_t297   grow_vector (unsigned size, hb_priority<0>)
298   {
299     for (; length < size; length++)
300       new (std::addressof (arrayZ[length])) Type ();
301   }
302   /* Specialization for hb_vector_t<hb_{vector,array}_t<U>> to speed up. */
303   template <typename T = Type,
304 	    hb_enable_if (hb_is_same (T, hb_vector_t<typename T::item_t>) ||
305 			  hb_is_same (T, hb_array_t <typename T::item_t>))>
306   void
grow_vectorhb_vector_t307   grow_vector (unsigned size, hb_priority<1>)
308   {
309     hb_memset (arrayZ + length, 0, (size - length) * sizeof (*arrayZ));
310     length = size;
311   }
312 
313   template <typename T = Type,
314 	    hb_enable_if (hb_is_trivially_copyable (T))>
315   void
copy_arrayhb_vector_t316   copy_array (hb_array_t<const Type> other)
317   {
318     length = other.length;
319     if (!HB_OPTIMIZE_SIZE_VAL && sizeof (T) >= sizeof (long long))
320       /* This runs faster because of alignment. */
321       for (unsigned i = 0; i < length; i++)
322 	arrayZ[i] = other.arrayZ[i];
323     else
324        hb_memcpy ((void *) arrayZ, (const void *) other.arrayZ, length * item_size);
325   }
326   template <typename T = Type,
327 	    hb_enable_if (!hb_is_trivially_copyable (T) &&
328 			   std::is_copy_constructible<T>::value)>
329   void
copy_arrayhb_vector_t330   copy_array (hb_array_t<const Type> other)
331   {
332     length = 0;
333     while (length < other.length)
334     {
335       length++;
336       new (std::addressof (arrayZ[length - 1])) Type (other.arrayZ[length - 1]);
337     }
338   }
339   template <typename T = Type,
340 	    hb_enable_if (!hb_is_trivially_copyable (T) &&
341 			  !std::is_copy_constructible<T>::value &&
342 			  std::is_default_constructible<T>::value &&
343 			  std::is_copy_assignable<T>::value)>
344   void
copy_arrayhb_vector_t345   copy_array (hb_array_t<const Type> other)
346   {
347     length = 0;
348     while (length < other.length)
349     {
350       length++;
351       new (std::addressof (arrayZ[length - 1])) Type ();
352       arrayZ[length - 1] = other.arrayZ[length - 1];
353     }
354   }
355 
356   void
shrink_vectorhb_vector_t357   shrink_vector (unsigned size)
358   {
359     assert (size <= length);
360     if (!std::is_trivially_destructible<Type>::value)
361     {
362       unsigned count = length - size;
363       Type *p = arrayZ + length - 1;
364       while (count--)
365         p--->~Type ();
366     }
367     length = size;
368   }
369 
370   void
shift_down_vectorhb_vector_t371   shift_down_vector (unsigned i)
372   {
373     for (; i < length; i++)
374       arrayZ[i - 1] = std::move (arrayZ[i]);
375   }
376 
377   /* Allocate for size but don't adjust length. */
allochb_vector_t378   bool alloc (unsigned int size, bool exact=false)
379   {
380     if (unlikely (in_error ()))
381       return false;
382 
383     unsigned int new_allocated;
384     if (exact)
385     {
386       /* If exact was specified, we allow shrinking the storage. */
387       size = hb_max (size, length);
388       if (size <= (unsigned) allocated &&
389 	  size >= (unsigned) allocated >> 2)
390 	return true;
391 
392       new_allocated = size;
393     }
394     else
395     {
396       if (likely (size <= (unsigned) allocated))
397 	return true;
398 
399       new_allocated = allocated;
400       while (size > new_allocated)
401 	new_allocated += (new_allocated >> 1) + 8;
402     }
403 
404 
405     /* Reallocate */
406 
407     bool overflows =
408       (int) in_error () ||
409       (new_allocated < size) ||
410       hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
411 
412     if (unlikely (overflows))
413     {
414       set_error ();
415       return false;
416     }
417 
418     Type *new_array = realloc_vector (new_allocated, hb_prioritize);
419 
420     if (unlikely (new_allocated && !new_array))
421     {
422       if (new_allocated <= (unsigned) allocated)
423         return true; // shrinking failed; it's okay; happens in our fuzzer
424 
425       set_error ();
426       return false;
427     }
428 
429     arrayZ = new_array;
430     allocated = new_allocated;
431 
432     return true;
433   }
434 
resizehb_vector_t435   bool resize (int size_, bool initialize = true, bool exact = false)
436   {
437     unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
438     if (!alloc (size, exact))
439       return false;
440 
441     if (size > length)
442     {
443       if (initialize)
444 	grow_vector (size, hb_prioritize);
445     }
446     else if (size < length)
447     {
448       if (initialize)
449 	shrink_vector (size);
450     }
451 
452     length = size;
453     return true;
454   }
resize_exacthb_vector_t455   bool resize_exact (int size_, bool initialize = true)
456   {
457     return resize (size_, initialize, true);
458   }
459 
pophb_vector_t460   Type pop ()
461   {
462     if (!length) return Null (Type);
463     Type v (std::move (arrayZ[length - 1]));
464     arrayZ[length - 1].~Type ();
465     length--;
466     return v;
467   }
468 
remove_orderedhb_vector_t469   void remove_ordered (unsigned int i)
470   {
471     if (unlikely (i >= length))
472       return;
473     shift_down_vector (i + 1);
474     arrayZ[length - 1].~Type ();
475     length--;
476   }
477 
478   template <bool Sorted = sorted,
479 	    hb_enable_if (!Sorted)>
remove_unorderedhb_vector_t480   void remove_unordered (unsigned int i)
481   {
482     if (unlikely (i >= length))
483       return;
484     if (i != length - 1)
485       arrayZ[i] = std::move (arrayZ[length - 1]);
486     arrayZ[length - 1].~Type ();
487     length--;
488   }
489 
shrinkhb_vector_t490   void shrink (int size_, bool shrink_memory = true)
491   {
492     unsigned int size = size_ < 0 ? 0u : (unsigned int) size_;
493     if (size >= length)
494       return;
495 
496     shrink_vector (size);
497 
498     if (shrink_memory)
499       alloc (size, true); /* To force shrinking memory if needed. */
500   }
501 
502 
503   /* Sorting API. */
qsorthb_vector_t504   void qsort (int (*cmp)(const void*, const void*) = Type::cmp)
505   { as_array ().qsort (cmp); }
506 
507   /* Unsorted search API. */
508   template <typename T>
lsearchhb_vector_t509   Type *lsearch (const T &x, Type *not_found = nullptr)
510   { return as_array ().lsearch (x, not_found); }
511   template <typename T>
lsearchhb_vector_t512   const Type *lsearch (const T &x, const Type *not_found = nullptr) const
513   { return as_array ().lsearch (x, not_found); }
514   template <typename T>
lfindhb_vector_t515   bool lfind (const T &x, unsigned *pos = nullptr) const
516   { return as_array ().lfind (x, pos); }
517 
518   /* Sorted search API. */
519   template <typename T,
520 	    bool Sorted=sorted, hb_enable_if (Sorted)>
bsearchhb_vector_t521   Type *bsearch (const T &x, Type *not_found = nullptr)
522   { return as_array ().bsearch (x, not_found); }
523   template <typename T,
524 	    bool Sorted=sorted, hb_enable_if (Sorted)>
bsearchhb_vector_t525   const Type *bsearch (const T &x, const Type *not_found = nullptr) const
526   { return as_array ().bsearch (x, not_found); }
527   template <typename T,
528 	    bool Sorted=sorted, hb_enable_if (Sorted)>
bfindhb_vector_t529   bool bfind (const T &x, unsigned int *i = nullptr,
530 	      hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE,
531 	      unsigned int to_store = (unsigned int) -1) const
532   { return as_array ().bfind (x, i, not_found, to_store); }
533 };
534 
535 template <typename Type>
536 using hb_sorted_vector_t = hb_vector_t<Type, true>;
537 
538 #endif /* HB_VECTOR_HH */
539