Quelle hb-vector.hh
Sprache: C
/*
* Copyright © 2017 , 2018 Google , Inc .
*
* This is part of HarfBuzz , a text shaping library .
*
* Permission is hereby granted , without written agreement and without
* license or royalty fees , to use , copy , modify , and distribute this
* software and its documentation for any purpose , provided that the
* above copyright notice and the following two paragraphs appear in
* all copies of this software .
*
* IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
* DIRECT , INDIRECT , SPECIAL , INCIDENTAL , OR CONSEQUENTIAL DAMAGES
* ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION , EVEN
* IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE .
*
* THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES , INCLUDING ,
* BUT NOT LIMITED TO , THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE . THE SOFTWARE PROVIDED HEREUNDER IS
* ON AN " AS IS " BASIS , AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
* PROVIDE MAINTENANCE , SUPPORT , UPDATES , ENHANCEMENTS , OR MODIFICATIONS .
*
* Google Author ( s ) : Behdad Esfahbod
*/
#ifndef HB_VECTOR_HH
#define HB_VECTOR_HH
#include "hb.hh"
#include "hb-array.hh"
#include "hb-meta.hh"
#include "hb-null.hh"
// Change to 1 to force inline vector allocs, to see callsite in malloc-stats tool.
#if 0
#define HB_ALWAYS_INLINE_VECTOR_ALLOCS HB_ALWAYS_INLINE
#else
#define HB_ALWAYS_INLINE_VECTOR_ALLOCS
#endif
template <typename Type,
bool sorted=false >
struct hb_vector_t
{
static constexpr bool realloc_move = true ;
typedef Type item_t;
static constexpr unsigned item_size = hb_static_size (Type);
using array_t = typename std::conditional<sorted, hb_sorted_array_t<Type>, hb_array_t<Type>>::type;
using c_array_t = typename std::conditional<sorted, hb_sorted_array_t<const Type>, hb_array_t<const Type>>::type;
hb_vector_t () = default ;
HB_ALWAYS_INLINE_VECTOR_ALLOCS
hb_vector_t (std::initializer_list<Type> lst) : hb_vector_t ()
{
alloc (lst.size (), true );
for (auto && item : lst)
push (item);
}
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
explicit hb_vector_t (const Iterable &o) : hb_vector_t ()
{
extend (o, true );
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
hb_vector_t (const hb_vector_t &o) : hb_vector_t ()
{
alloc_exact (o.length);
if (unlikely (in_error ())) return ;
copy_array (o.as_array ());
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
hb_vector_t (array_t o) : hb_vector_t ()
{
alloc_exact (o.length);
if (unlikely (in_error ())) return ;
copy_array (o);
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
hb_vector_t (c_array_t o) : hb_vector_t ()
{
alloc_exact (o.length);
if (unlikely (in_error ())) return ;
copy_array (o);
}
hb_vector_t (hb_vector_t &&o) noexcept
{
allocated = o.allocated;
length = o.length;
arrayZ = o.arrayZ;
o.init ();
}
~hb_vector_t () { fini (); }
template <unsigned n>
void
set_storage (Type (&array)[n])
{ set_storage (array, n); }
void
set_storage (hb_array_t<Type> array)
{ set_storage (array.arrayZ, array.length); }
template <typename T = Type,
hb_enable_if (hb_is_trivially_constructible(T) &&
hb_is_trivially_destructible(T))>
void
set_storage (Type *array, unsigned n)
{
assert (allocated == 0 );
assert (length == 0 );
arrayZ = array;
length = n;
}
template <typename Iterable,
hb_requires (hb_is_iterable (Iterable))>
HB_ALWAYS_INLINE_VECTOR_ALLOCS
void extend (const Iterable &o, bool exact=false )
{
auto iter = hb_iter (o);
if (iter.is_random_access_iterator || iter.has_fast_len)
{
if (unlikely (!alloc (length + hb_len (iter), exact)))
return ;
unsigned count = hb_len (iter);
for (unsigned i = 0 ; i < count; i++)
push_has_room (*iter++);
}
while (iter)
{
if (unlikely (!alloc (length + 1 )))
return ;
unsigned room = allocated - length;
for (unsigned i = 0 ; i < room && iter; i++)
push_has_room (*iter++);
}
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
void extend (array_t o, bool exact=false )
{
alloc (length + o.length, exact);
if (unlikely (in_error ())) return ;
copy_array (o);
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
void extend (c_array_t o, bool exact=false )
{
alloc (length + o.length, exact);
if (unlikely (in_error ())) return ;
copy_array (o);
}
public :
int allocated = 0 ; /* < 0 means allocation failed. */
unsigned int length = 0 ;
public :
Type *arrayZ = nullptr;
void init ()
{
allocated = length = 0 ;
arrayZ = nullptr;
}
void init0 ()
{
}
void fini ()
{
if (is_owned ())
{
shrink_vector (0 );
hb_free (arrayZ);
}
init ();
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
hb_vector_t &reset ()
{
if (unlikely (in_error ()))
reset_error ();
clear ();
return *this ;
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
hb_vector_t &reset_if_error ()
{
if (unlikely (in_error ()))
reset ();
return *this ;
}
/* Transfer ownership of the backing storage to caller.
* Returns nullptr if storage is not owned by this vector. */
Type *
steal (unsigned *len = nullptr, int *allocated_out = nullptr)
{
if (!is_owned ())
return nullptr;
if (len)
*len = length;
if (allocated_out)
*allocated_out = allocated;
Type *p = arrayZ;
init ();
return p;
}
/* Adopt a previously detached owned buffer. */
void
recycle_buffer (Type *buffer,
unsigned len,
int allocated_len)
{
fini ();
arrayZ = buffer;
length = len;
allocated = allocated_len;
}
friend void swap (hb_vector_t& a, hb_vector_t& b) noexcept
{
hb_swap (a.allocated, b.allocated);
hb_swap (a.length, b.length);
hb_swap (a.arrayZ, b.arrayZ);
}
hb_vector_t& operator = (const hb_vector_t &o)
{
reset ();
alloc_exact (o.length);
if (unlikely (in_error ())) return *this ;
length = 0 ;
copy_array (o.as_array ());
return *this ;
}
hb_vector_t& operator = (hb_vector_t &&o) noexcept
{
hb_swap (*this , o);
return *this ;
}
hb_bytes_t as_bytes () const
{ return hb_bytes_t ((const char *) arrayZ, get_size ()); }
bool operator == (const hb_vector_t &o) const { return as_array () == o.as_array (); }
bool operator != (const hb_vector_t &o) const { return !(*this == o); }
uint32_t hash () const { return as_array ().hash (); }
Type& operator [] (int i_)
{
unsigned int i = (unsigned int ) i_;
if (unlikely (i >= length))
return Crap (Type);
return arrayZ[i];
}
const Type& operator [] (int i_) const
{
unsigned int i = (unsigned int ) i_;
if (unlikely (i >= length))
return Null (Type);
return arrayZ[i];
}
Type& tail () { return (*this )[length - 1 ]; }
const Type& tail () const { return (*this )[length - 1 ]; }
explicit operator bool () const { return length; }
size_t get_size () const { return length * item_size; }
/* Sink interface. */
template <typename T>
hb_vector_t& operator << (T&& v) { push (std::forward<T> (v)); return *this ; }
array_t as_array () { return hb_array (arrayZ, length); }
c_array_t as_array () const { return hb_array (arrayZ, length); }
/* Iterator. */
typedef c_array_t iter_t;
typedef array_t writer_t;
iter_t iter () const { return as_array (); }
writer_t writer () { return as_array (); }
operator iter_t () const { return iter (); }
operator writer_t () { return writer (); }
/* Faster range-based for loop. */
Type *begin () const { return arrayZ; }
Type *end () const { return arrayZ + length; }
hb_sorted_array_t<Type> as_sorted_array ()
{ return hb_sorted_array (arrayZ, length); }
hb_sorted_array_t<const Type> as_sorted_array () const
{ return hb_sorted_array (arrayZ, length); }
template <typename T> explicit operator T * () { return arrayZ; }
template <typename T> explicit operator const T * () const { return arrayZ; }
Type * operator + (unsigned int i) { return arrayZ + i; }
const Type * operator + (unsigned int i) const { return arrayZ + i; }
HB_ALWAYS_INLINE_VECTOR_ALLOCS
Type *push ()
{
if (unlikely (!resize (length + 1 )))
return std::addressof (Crap (Type));
return std::addressof (arrayZ[length - 1 ]);
}
template <typename ... Args>
HB_ALWAYS_INLINE_VECTOR_ALLOCS
Type *push (Args&&... args)
{
if (unlikely ((int ) length >= allocated && !alloc (length + 1 )))
// If push failed to allocate then don't copy v, since this may cause
// the created copy to leak memory since we won't have stored a
// reference to it.
return std::addressof (Crap (Type));
return push_has_room (std::forward<Args> (args)...);
}
template <typename ... Args>
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool push_or_fail (Args&&... args)
{
return push (std::forward<Args> (args)...) != std::addressof (Crap (Type));
}
template <typename ... Args>
HB_ALWAYS_INLINE_VECTOR_ALLOCS
Type *push_has_room (Args&&... args)
{
/* Emplace. */
Type *p = std::addressof (arrayZ[length++]);
return new (p) Type (std::forward<Args> (args)...);
}
bool is_owned () const
{
return allocated != 0 && allocated != -1 ;
}
bool in_error () const { return allocated < 0 ; }
void set_error ()
{
assert (allocated >= 0 );
allocated = -allocated - 1 ;
}
void reset_error ()
{
assert (allocated < 0 );
allocated = -(allocated + 1 );
}
void ensure_error ()
{
if (!in_error ())
set_error ();
}
Type *
_realloc (unsigned new_allocated)
{
if (!new_allocated)
{
if (is_owned ())
hb_free (arrayZ);
return nullptr;
}
if (!allocated && arrayZ)
{
/* If we have a non-null arrayZ but allocated is 0, then we are
* reallocating from a foreign array. */
Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type));
if (unlikely (!new_array))
return nullptr;
hb_memcpy ((void *) new_array, (const void *) arrayZ, length * sizeof (Type));
return new_array;
}
return (Type *) hb_realloc (arrayZ, new_allocated * sizeof (Type));
}
Type *
_malloc_move (unsigned new_allocated)
{
if (!new_allocated)
{
if (is_owned ())
hb_free (arrayZ);
return nullptr;
}
Type *new_array = (Type *) hb_malloc (new_allocated * sizeof (Type));
if (likely (new_array))
{
for (unsigned i = 0 ; i < length; i++)
{
new (std::addressof (new_array[i])) Type ();
new_array[i] = std::move (arrayZ[i]);
arrayZ[i].~Type ();
}
if (is_owned ())
hb_free (arrayZ);
}
return new_array;
}
template <typename T = Type,
hb_enable_if (hb_is_trivially_copy_assignable(T))>
Type *
realloc_vector (unsigned new_allocated, hb_priority<0 >)
{
return _realloc (new_allocated);
}
template <typename T = Type,
hb_enable_if (!hb_is_trivially_copy_assignable(T))>
Type *
realloc_vector (unsigned new_allocated, hb_priority<0 >)
{
return _malloc_move (new_allocated);
}
/* Specialization for types that can be moved using realloc(). */
template <typename T = Type,
hb_enable_if (T::realloc_move)>
Type *
realloc_vector (unsigned new_allocated, hb_priority<1 >)
{
return _realloc (new_allocated);
}
template <typename T = Type,
hb_enable_if (hb_is_trivially_constructible(T))>
void
grow_vector (unsigned size, hb_priority<0 >)
{
hb_memset (arrayZ + length, 0 , (size - length) * sizeof (*arrayZ));
length = size;
}
template <typename T = Type,
hb_enable_if (!hb_is_trivially_constructible(T))>
void
grow_vector (unsigned size, hb_priority<0 >)
{
for (; length < size; length++)
new (std::addressof (arrayZ[length])) Type ();
}
/* Specialization for hb_vector_t<hb_{vector,array}_t<U>> to speed up. */
template <typename T = Type,
hb_enable_if (hb_is_same (T, hb_vector_t<typename T::item_t>) ||
hb_is_same (T, hb_array_t <typename T::item_t>))>
void
grow_vector (unsigned size, hb_priority<1 >)
{
hb_memset (arrayZ + length, 0 , (size - length) * sizeof (*arrayZ));
length = size;
}
template <typename T = Type,
hb_enable_if (hb_is_trivially_copyable (T))>
void
copy_array (hb_array_t<Type> other)
{
hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size);
length += other.length;
}
template <typename T = Type,
hb_enable_if (hb_is_trivially_copyable (T))>
void
copy_array (hb_array_t<const Type> other)
{
hb_memcpy ((void *) (arrayZ + length), (const void *) other.arrayZ, other.length * item_size);
length += other.length;
}
template <typename T = Type,
hb_enable_if (!hb_is_trivially_copyable (T) &&
std::is_copy_constructible<T>::value)>
void
copy_array (hb_array_t<const Type> other)
{
for (unsigned i = 0 ; i < other.length; i++)
new (std::addressof (arrayZ[length + i])) Type (other.arrayZ[i]);
length += other.length;
}
template <typename T = Type,
hb_enable_if (!hb_is_trivially_copyable (T) &&
!std::is_copy_constructible<T>::value &&
std::is_default_constructible<T>::value &&
std::is_copy_assignable<T>::value)>
void
copy_array (hb_array_t<const Type> other)
{
for (unsigned i = 0 ; i < other.length; i++)
{
new (std::addressof (arrayZ[length + i])) Type ();
arrayZ[length + i] = other.arrayZ[i];
}
length += other.length;
}
void
shrink_vector (unsigned size)
{
assert (size <= length);
if (!hb_is_trivially_destructible(Type))
{
unsigned count = length - size;
Type *p = arrayZ + length;
while (count--)
(--p)->~Type ();
}
length = size;
}
void
shift_down_vector (unsigned i)
{
for (; i < length; i++)
arrayZ[i - 1 ] = std::move (arrayZ[i]);
}
/* Allocate for size but don't adjust length. */
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool alloc (unsigned int size, bool exact=false )
{
if (unlikely (in_error ()))
return false ;
unsigned int new_allocated;
if (exact)
{
/* If exact was specified, we allow shrinking the storage. */
size = hb_max (size, length);
if (size <= (unsigned ) allocated &&
size >= (unsigned ) allocated >> 2 )
return true ;
new_allocated = size;
}
else
{
if (likely (size <= (unsigned ) allocated))
return true ;
new_allocated = allocated;
while (size > new_allocated)
new_allocated += (new_allocated >> 1 ) + 8 ;
}
/* Reallocate */
bool overflows =
(int ) in_error () ||
(new_allocated < size) ||
hb_unsigned_mul_overflows (new_allocated, sizeof (Type));
if (unlikely (overflows))
{
set_error ();
return false ;
}
Type *new_array = realloc_vector (new_allocated, hb_prioritize);
if (unlikely (new_allocated && !new_array))
{
if (new_allocated <= (unsigned ) allocated)
return true ; // shrinking failed; it's okay; happens in our fuzzer
set_error ();
return false ;
}
arrayZ = new_array;
allocated = new_allocated;
return true ;
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool alloc_exact (unsigned int size)
{
return alloc (size, true );
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
void clear ()
{
shrink_vector (0 );
}
template <typename allocator_t>
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool allocate_from_pool (allocator_t *allocator, unsigned size, unsigned int initialize = true )
{
if (allocator)
{
assert (!length && !allocated);
arrayZ = (Type *) allocator->alloc (size * sizeof (Type), alignof (Type));
if (unlikely (!arrayZ))
{
set_error ();
return false ;
}
if (initialize)
grow_vector (size, hb_prioritize);
else
length = size;
return true ;
}
return resize_full ((int ) size, initialize, true );
}
template <typename allocator_t>
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool duplicate_vector_from_pool (allocator_t *allocator, const hb_vector_t &other)
{
if (unlikely (!allocate_from_pool (allocator, other.length, false )))
return false ;
length = 0 ;
copy_array (other.as_array ());
return true ;
}
template <typename allocator_t>
void shrink_back_to_pool (allocator_t *allocator, int size)
{
unsigned orig_length = length;
shrink (size, false );
if (allocator && !is_owned ())
allocator->discard (arrayZ + length, (orig_length - length) * sizeof (Type));
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool resize_full (int size_, bool initialize, bool exact)
{
unsigned int size = size_ < 0 ? 0 u : (unsigned int ) size_;
if (!alloc (size, exact))
return false ;
if (size > length)
{
if (initialize)
grow_vector (size, hb_prioritize);
}
else if (size < length)
{
if (initialize)
shrink_vector (size);
}
length = size;
return true ;
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool resize (int size_)
{
return resize_full (size_, true , false );
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool resize_dirty (int size_)
{
return resize_full (size_, false , false );
}
HB_ALWAYS_INLINE_VECTOR_ALLOCS
bool resize_exact (int size_)
{
return resize_full (size_, true , true );
}
Type pop ()
{
if (!length) return Null (Type);
Type v (std::move (arrayZ[length - 1 ]));
arrayZ[length - 1 ].~Type ();
length--;
return v;
}
void remove_ordered (unsigned int i)
{
if (unlikely (i >= length))
return ;
shift_down_vector (i + 1 );
arrayZ[length - 1 ].~Type ();
length--;
}
template <bool Sorted = sorted,
hb_enable_if (!Sorted)>
void remove_unordered (unsigned int i)
{
if (unlikely (i >= length))
return ;
if (i != length - 1 )
arrayZ[i] = std::move (arrayZ[length - 1 ]);
arrayZ[length - 1 ].~Type ();
length--;
}
void shrink (int size_, bool shrink_memory = true )
{
unsigned int size = size_ < 0 ? 0 u : (unsigned int ) size_;
if (size >= length)
return ;
shrink_vector (size);
if (is_owned () && shrink_memory)
alloc_exact (size); /* To force shrinking memory if needed. */
}
/* Sorting API. */
template <typename Compar>
void qsort (Compar compar)
{ as_array ().qsort (compar); }
void qsort ()
{ as_array ().qsort (); }
/* Unsorted search API. */
template <typename T>
Type *lsearch (const T &x, Type *not_found = nullptr)
{ return as_array ().lsearch (x, not_found); }
template <typename T>
const Type *lsearch (const T &x, const Type *not_found = nullptr) const
{ return as_array ().lsearch (x, not_found); }
template <typename T>
bool lfind (const T &x, unsigned *pos = nullptr) const
{ return as_array ().lfind (x, pos); }
/* Sorted search API. */
template <typename T,
bool Sorted=sorted, hb_enable_if (Sorted)>
Type *bsearch (const T &x, Type *not_found = nullptr)
{ return as_array ().bsearch (x, not_found); }
template <typename T,
bool Sorted=sorted, hb_enable_if (Sorted)>
const Type *bsearch (const T &x, const Type *not_found = nullptr) const
{ return as_array ().bsearch (x, not_found); }
template <typename T,
bool Sorted=sorted, hb_enable_if (Sorted)>
bool bfind (const T &x, unsigned int *i = nullptr,
hb_not_found_t not_found = HB_NOT_FOUND_DONT_STORE,
unsigned int to_store = (unsigned int ) -1 ) const
{ return as_array ().bfind (x, i, not_found, to_store); }
};
template <typename Type>
using hb_sorted_vector_t = hb_vector_t<Type, true >;
#endif /* HB_VECTOR_HH */
Messung V0.5 in Prozent C=97 H=96 G=96
¤ Dauer der Verarbeitung: 0.14 Sekunden
(vorverarbeitet am 2026-09-30)
¤
*© Formatika GbR, Deutschland
2026-10-10
Neuigkeiten
Aktuelles
Motto des Tages
Open Source Software
Jenseits des Üblichen ....
Besucherstatistik
Statistik
#Sources=1126864
#Domains=1897691