29 #ifndef PIRANHA_SMALL_VECTOR_HPP 30 #define PIRANHA_SMALL_VECTOR_HPP 35 #include <initializer_list> 41 #include <type_traits> 45 #include <piranha/config.hpp> 46 #include <piranha/detail/small_vector_fwd.hpp> 47 #include <piranha/detail/vector_hasher.hpp> 49 #include <piranha/math.hpp> 51 #include <piranha/s11n.hpp> 52 #include <piranha/safe_cast.hpp> 53 #include <piranha/static_vector.hpp> 89 using size_type =
unsigned char;
93 using pointer = value_type *;
94 using const_pointer = value_type
const *;
97 static const std::size_t max_alloc_size = std::numeric_limits<std::size_t>::max() /
sizeof(value_type);
98 static const bool need_reserve_check = std::numeric_limits<size_type>::max() > max_alloc_size;
99 static bool reserve_check_size(
const size_type &,
const std::false_type &)
103 static bool reserve_check_size(
const size_type &new_capacity,
const std::true_type &)
105 return new_capacity > max_alloc_size;
109 static const size_type max_size = std::numeric_limits<size_type>::max();
110 using iterator = pointer;
111 using const_iterator = const_pointer;
112 dynamic_storage() : m_tag(0u), m_size(0u), m_capacity(0u), m_ptr(nullptr)
115 dynamic_storage(dynamic_storage &&other) noexcept
116 : m_tag(0u), m_size(other.m_size), m_capacity(other.m_capacity), m_ptr(other.m_ptr)
120 other.m_capacity = 0u;
121 other.m_ptr =
nullptr;
123 dynamic_storage(
const dynamic_storage &other)
124 : m_tag(0u), m_size(0u),
125 m_capacity(other.m_size),
129 m_ptr = allocate(other.m_size);
132 for (; m_size < other.m_size; ++m_size) {
133 construct(m_ptr + m_size, other[m_size]);
137 destroy_and_deallocate();
146 piranha_assert(m_tag == 0u);
147 destroy_and_deallocate();
149 dynamic_storage &operator=(dynamic_storage &&other) noexcept
151 if (likely(
this != &other)) {
153 destroy_and_deallocate();
155 m_size = other.m_size;
156 m_capacity = other.m_capacity;
160 other.m_capacity = 0u;
161 other.m_ptr =
nullptr;
165 dynamic_storage &operator=(
const dynamic_storage &other)
167 if (likely(
this != &other)) {
168 dynamic_storage tmp(other);
169 *
this = std::move(tmp);
177 void push_back(
const value_type &x)
181 void push_back(value_type &&x)
183 push_back_impl(std::move(x));
185 value_type &operator[](
const size_type &n)
189 const value_type &operator[](
const size_type &n)
const 193 size_type size()
const 205 return m_ptr + m_size;
207 const_iterator begin()
const 211 const_iterator end()
const 213 return m_ptr + m_size;
215 void reserve(
const size_type &new_capacity)
217 piranha_assert(consistency_checks());
219 if (new_capacity <= m_capacity) {
224 if (unlikely( reserve_check_size(
225 new_capacity, std::integral_constant<bool, need_reserve_check>()))) {
229 pointer new_storage = allocate(new_capacity);
230 assert(new_storage !=
nullptr);
233 for (size_type i = 0u; i < m_size; ++i) {
234 construct(new_storage + i, std::move((*
this)[i]));
237 destroy_and_deallocate();
239 m_capacity = new_capacity;
242 size_type capacity()
const 246 std::size_t hash()
const 248 return detail::vector_hasher(*
this);
250 void resize(
const size_type &new_size)
256 if (new_size == m_size) {
261 const bool new_storage = (m_capacity < new_size);
262 pointer storage = new_storage ? allocate(new_size) : m_ptr;
267 piranha_assert(storage !=
nullptr);
270 size_type i = m_size;
272 for (; i < new_size; ++i) {
273 construct(storage + i);
277 for (size_type j = m_size; j < i; ++j) {
278 destroy(storage + j);
288 for (size_type j = 0u; j < m_size; ++j) {
289 construct(storage + j, std::move((*
this)[j]));
292 destroy_and_deallocate();
293 m_capacity = new_size;
297 for (size_type j = new_size; j < m_size; ++j) {
298 destroy(storage + j);
305 iterator erase(const_iterator it)
307 piranha_assert(it != end());
308 auto retval =
const_cast<iterator
>(it);
309 const auto it_f = end() - 1;
310 for (; it != it_f; ++it) {
312 ::new (static_cast<void *>(const_cast<iterator>(it))) value_type(std::move(*(it + 1)));
315 m_size =
static_cast<size_type
>(m_size - 1u);
320 template <
typename... Args>
321 static void construct(pointer p, Args &&... args)
323 ::new (static_cast<void *>(p)) value_type(std::forward<Args>(args)...);
325 static void destroy(pointer p)
331 static pointer allocate(
const size_type &s)
333 return static_cast<pointer
>(
aligned_palloc(0u, static_cast<std::size_t>(s *
sizeof(value_type))));
335 static void deallocate(pointer p)
340 template <
typename U>
341 void push_back_impl(U &&x)
343 piranha_assert(consistency_checks());
344 if (unlikely(m_capacity == m_size)) {
347 construct(m_ptr + m_size, std::forward<U>(x));
350 void destroy_and_deallocate()
352 piranha_assert(consistency_checks());
353 for (size_type i = 0u; i < m_size; ++i) {
363 void increase_capacity()
365 if (unlikely(m_capacity == max_size)) {
369 const size_type new_capacity
370 = (m_capacity > max_size / 2u) ? max_size : ((m_capacity != 0u) ?
static_cast<size_type
>(m_capacity * 2u)
371 : static_cast<size_type>(1u));
372 reserve(new_capacity);
374 bool consistency_checks()
377 if (unlikely(m_size > m_capacity)) {
381 if (unlikely(m_ptr !=
nullptr && m_capacity == 0u)) {
385 if (unlikely(m_ptr ==
nullptr && m_capacity != 0u)) {
394 size_type m_capacity;
398 template <
typename T>
399 const typename dynamic_storage<T>::size_type dynamic_storage<T>::max_size;
401 template <
typename T>
402 const std::size_t dynamic_storage<T>::max_alloc_size;
405 template <
typename T, std::
size_t Size = 1u,
typename =
void>
406 struct auto_static_size {
407 static const std::size_t value = Size;
410 template <
typename T, std::
size_t Size>
411 struct auto_static_size<T, Size,
412 typename
std::enable_if<(sizeof(dynamic_storage<T>) > sizeof(static_vector<T, Size>))>::type> {
413 static_assert(Size < std::numeric_limits<std::size_t>::max(),
"Overflow error in auto_static_size.");
414 static const std::size_t value = auto_static_size<T, Size + 1u>::value;
417 template <
typename T>
418 struct check_integral_constant {
419 static const bool value =
false;
422 template <std::
size_t Size>
423 struct check_integral_constant<
std::integral_constant<std::size_t, Size>> {
424 static const bool value =
true;
435 template <
typename T,
typename S>
436 union small_vector_union {
437 using s_storage =
typename std::conditional<S::value == 0u, static_vector<T, auto_static_size<T>::value>,
438 static_vector<T, S::value>>::type;
439 using d_storage = dynamic_storage<T>;
441 small_vector_union() : m_st()
444 small_vector_union(
const small_vector_union &other)
446 if (other.is_static()) {
447 ::new (static_cast<void *>(&m_st)) s_storage(other.g_st());
449 ::new (static_cast<void *>(&m_dy)) d_storage(other.g_dy());
452 small_vector_union(small_vector_union &&other) noexcept
454 if (other.is_static()) {
455 ::new (static_cast<void *>(&m_st)) s_storage(std::move(other.g_st()));
457 ::new (static_cast<void *>(&m_dy)) d_storage(std::move(other.g_dy()));
460 ~small_vector_union()
471 small_vector_union &operator=(small_vector_union &&other) noexcept
473 if (unlikely(
this == &other)) {
476 if (is_static() == other.is_static()) {
478 g_st() = std::move(other.g_st());
480 g_dy() = std::move(other.g_dy());
488 ::new (static_cast<void *>(&m_dy)) d_storage(std::move(other.g_dy()));
492 ::new (static_cast<void *>(&m_st)) s_storage(std::move(other.g_st()));
497 small_vector_union &operator=(
const small_vector_union &other)
499 if (likely(
this != &other)) {
500 *
this = small_vector_union(other);
504 bool is_static()
const 506 return static_cast<bool>(m_st.m_tag);
509 const s_storage &g_st()
const 511 piranha_assert(is_static());
516 piranha_assert(is_static());
519 const d_storage &g_dy()
const 521 piranha_assert(!is_static());
526 piranha_assert(!is_static());
562 template <
typename T,
typename S = std::
integral_constant<std::
size_t, 0u>>
565 PIRANHA_TT_CHECK(is_container_element, T);
566 PIRANHA_TT_CHECK(detail::check_integral_constant, S);
567 using u_type = detail::small_vector_union<T, S>;
568 using s_storage =
typename u_type::s_storage;
569 using d_storage =
typename u_type::d_storage;
571 using size_type_impl = max_int<typename s_storage::size_type, typename d_storage::size_type>;
572 template <
typename U>
573 static constexpr U get_max(
const U &a,
const U &b)
575 return (a > b) ? a : b;
580 static const typename std::decay<decltype(s_storage::max_size)>::type
max_static_size = s_storage::max_size;
582 static const typename std::decay<decltype(d_storage::max_size)>::type
max_dynamic_size = d_storage::max_size;
588 static const bool need_resize_check = std::numeric_limits<size_type>::max() > d_storage::max_size;
589 static bool resize_check_size(
const size_type &,
const std::false_type &)
593 static bool resize_check_size(
const size_type &
size,
const std::true_type &)
595 return size > d_storage::max_size;
613 template <
typename U>
614 using init_list_enabler =
typename std::enable_if<has_safe_cast<T, U>::value,
int>::type;
616 template <
typename U>
617 using equality_enabler =
typename std::enable_if<is_equality_comparable<U>::value,
int>::type;
619 template <
typename U>
620 using hash_enabler =
typename std::enable_if<is_hashable<U>::value,
int>::type;
622 template <
typename U>
623 using add_enabler =
typename std::enable_if<has_add3<U>::value,
int>::type;
625 template <
typename U>
626 using sub_enabler =
typename std::enable_if<has_sub3<U>::value,
int>::type;
628 friend class boost::serialization::access;
629 template <
class Archive>
630 void save(Archive &ar,
unsigned)
const 632 boost_save_vector(ar, *
this);
634 template <
class Archive>
635 void load(Archive &ar,
unsigned)
637 boost_load_vector(ar, *
this);
639 BOOST_SERIALIZATION_SPLIT_MEMBER()
675 template <typename U, init_list_enabler<U> = 0>
679 for (
const U &x : l) {
755 push_back_impl(std::move(x));
763 if (m_union.is_static()) {
764 return m_union.g_st().begin();
766 return m_union.g_dy().begin();
775 if (m_union.is_static()) {
776 return m_union.g_st().end();
778 return m_union.g_dy().end();
787 if (m_union.is_static()) {
788 return m_union.g_st().begin();
790 return m_union.g_dy().begin();
799 if (m_union.is_static()) {
800 return m_union.g_st().end();
802 return m_union.g_dy().end();
811 if (m_union.is_static()) {
812 return m_union.g_st().size();
814 return m_union.g_dy().size();
823 return m_union.is_static();
837 template <
typename U = value_type, equality_enabler<U> = 0>
845 =
static_cast<unsigned>(m_union.is_static()) + (static_cast<unsigned>(other.m_union.is_static()) << 1u);
848 return m_union.g_dy().size() == other.m_union.g_dy().size()
849 && std::equal(m_union.g_dy().begin(), m_union.g_dy().end(), other.m_union.g_dy().begin());
851 return m_union.g_st().size() == other.m_union.g_dy().size()
852 && std::equal(m_union.g_st().begin(), m_union.g_st().end(), other.m_union.g_dy().begin());
854 return m_union.g_dy().size() == other.m_union.g_st().size()
855 && std::equal(m_union.g_dy().begin(), m_union.g_dy().end(), other.m_union.g_st().begin());
857 return m_union.g_st().size() == other.m_union.g_st().size()
858 && std::equal(m_union.g_st().begin(), m_union.g_st().end(), other.m_union.g_st().begin());
871 template <
typename U = value_type, equality_enabler<U> = 0>
883 template <
typename U = value_type, hash_enabler<U> = 0>
886 if (m_union.is_static()) {
887 return m_union.g_st().hash();
889 return m_union.g_dy().hash();
898 if (m_union.is_static()) {
899 return m_union.g_st().empty();
901 return m_union.g_dy().empty();
916 if (m_union.is_static()) {
918 m_union.g_st().resize(static_cast<typename s_storage::size_type>(
size));
923 if (unlikely(resize_check_size(
size, std::integral_constant<bool, need_resize_check>()))) {
927 const auto d_size =
static_cast<typename d_storage::size_type
>(
size);
929 tmp_d.reserve(d_size);
932 std::move(m_union.g_st().begin(), m_union.g_st().end(), std::back_inserter(tmp_d));
934 tmp_d.resize(d_size);
936 m_union.g_st().~s_storage();
937 ::new (static_cast<void *>(&(m_union.m_dy))) d_storage(std::move(tmp_d));
940 if (unlikely(resize_check_size(
size, std::integral_constant<bool, need_resize_check>()))) {
943 m_union.g_dy().resize(static_cast<typename d_storage::size_type>(
size));
966 template <
typename U = value_type, add_enabler<U> = 0>
969 const auto sbe1 =
size_begin_end(), sbe2 = other.size_begin_end();
970 if (unlikely(std::get<0u>(sbe1) != std::get<0u>(sbe2))) {
971 piranha_throw(std::invalid_argument,
"vector size mismatch");
976 retval.resize(std::get<0u>(sbe1));
977 auto sbe_out = retval.size_begin_end();
978 for (
size_type i = 0u; i < std::get<0u>(sbe1); ++i) {
979 math::add3(*(std::get<1u>(sbe_out) + i), *(std::get<1u>(sbe1) + i), *(std::get<1u>(sbe2) + i));
1002 template <
typename U = value_type, sub_enabler<U> = 0>
1005 const auto sbe1 =
size_begin_end(), sbe2 = other.size_begin_end();
1006 if (unlikely(std::get<0u>(sbe1) != std::get<0u>(sbe2))) {
1007 piranha_throw(std::invalid_argument,
"vector size mismatch");
1009 retval.resize(std::get<0u>(sbe1));
1010 auto sbe_out = retval.size_begin_end();
1011 for (
size_type i = 0u; i < std::get<0u>(sbe1); ++i) {
1012 math::sub3(*(std::get<1u>(sbe_out) + i), *(std::get<1u>(sbe1) + i), *(std::get<1u>(sbe2) + i));
1029 if (m_union.is_static()) {
1030 return m_union.g_st().erase(it);
1032 return m_union.g_dy().erase(it);
1041 if (m_union.is_static()) {
1042 return std::make_tuple(
size_type(m_union.g_st().size()), m_union.g_st().begin(), m_union.g_st().end());
1044 return std::make_tuple(
size_type(m_union.g_dy().size()), m_union.g_dy().begin(), m_union.g_dy().end());
1053 if (m_union.is_static()) {
1054 return std::make_tuple(
size_type(m_union.g_st().size()), m_union.g_st().begin(), m_union.g_st().end());
1056 return std::make_tuple(
size_type(m_union.g_dy().size()), m_union.g_dy().begin(), m_union.g_dy().end());
1061 template <
typename U>
1062 void push_back_impl(U &&x)
1064 if (m_union.is_static()) {
1065 if (m_union.g_st().size() == m_union.g_st().max_size) {
1068 using d_size_type =
typename d_storage::size_type;
1075 if (unlikely(s_storage::max_size >= d_storage::max_size)) {
1079 tmp_d.reserve(static_cast<d_size_type>(static_cast<d_size_type>(m_union.g_st().max_size) + 1u));
1080 std::move(m_union.g_st().begin(), m_union.g_st().end(), std::back_inserter(tmp_d));
1082 tmp_d.push_back(std::forward<U>(x));
1086 m_union.g_st().~s_storage();
1087 ::new (static_cast<void *>(&(m_union.m_dy))) d_storage(
std::move(tmp_d));
1089 m_union.g_st().push_back(std::forward<U>(x));
1093 m_union.g_dy().push_back(std::forward<U>(x));
1101 template <
typename T,
typename S>
1104 template <
typename T,
typename S>
1107 template <
typename T,
typename S>
1118 template <
typename Archive,
typename T, std::
size_t Size>
1120 boost_save_vector_enabler<Archive, small_vector<T, std::integral_constant<std::size_t, Size>>>>
1136 template <
typename Archive,
typename T, std::
size_t Size>
1138 boost_load_vector_enabler<Archive, small_vector<T, std::integral_constant<std::size_t, Size>>>>
1142 #if defined(PIRANHA_WITH_MSGPACK) 1152 template <
typename Stream,
typename T, std::
size_t Size>
1154 msgpack_pack_vector_enabler<Stream,
1155 small_vector<T, std::integral_constant<std::size_t, Size>>>> {
1172 msgpack_pack_vector(packer, v, f);
1183 template <
typename T, std::
size_t Size>
1185 msgpack_convert_array_enabler<small_vector<T, std::integral_constant<std::size_t, Size>>>> {
1204 msgpack_convert_array(o, v, f);
const_iterator end() const
Const end iterator.
void * aligned_palloc(const std::size_t &alignment, const std::size_t &size)
Allocate memory aligned to a specific value.
static const std::decay< decltype(d_storage::max_size)>::type max_dynamic_size
Maximum number of elements that can be stored in dynamic storage.
Default implementation of piranha::boost_load().
void push_back(const value_type &x)
Copy-add element at the end.
iterator begin()
Mutable begin iterator.
bool operator!=(const small_vector &other) const
Inequality operator.
auto add3(T &a, const T &b, const T &c) -> decltype(add3_impl< T >()(a, b, c))
Ternary addition.
bool is_static() const
Static storage flag.
Implementation of piranha::boost_load() via the Boost API.
std::tuple< size_type, const_iterator, const_iterator > size_begin_end() const
Size, begin and end (const version).
Default implementation of piranha::boost_save().
small_vector()=default
Default constructor.
~small_vector()=default
Destructor.
value_type const * const_iterator
Const iterator type.
static const size_type max_size
Maximum number of elements that can be stored.
Forward iterator type trait.
size_type size() const
Size.
Default functor for the implementation of piranha::msgpack_convert().
#define piranha_throw(exception_type,...)
Exception-throwing macro.
iterator end()
Mutable end iterator.
Default functor for the implementation of piranha::msgpack_pack().
msgpack_format
Serialization format for msgpack.
iterator erase(const_iterator it)
Erase element.
value_type * iterator
Iterator type.
const_iterator begin() const
Const begin iterator.
void operator()(small_vector< T, std::integral_constant< std::size_t, Size >> &v, const msgpack::object &o, msgpack_format f) const
Call operator.
void resize(const size_type &size)
Resize.
Implementation of piranha::boost_save() via the Boost API.
bool empty() const
Empty test.
void push_back(value_type &&x)
Move-add element at the end.
void aligned_pfree(const std::size_t &alignment, void *ptr)
Free memory allocated via piranha::aligned_alloc.
small_vector(const size_type &size, const T &value)
Constructor from size and value.
bool operator==(const small_vector &other) const
Equality operator.
std::size_t hash() const
Hash method.
small_vector & operator=(const small_vector &)=default
Copy assignment operator.
const value_type & operator[](const size_type &n) const
Const subscript operator.
auto sub3(T &a, const T &b, const T &c) -> decltype(sub3_impl< T >()(a, b, c))
Ternary subtraction.
size_type_impl size_type
A fundamental unsigned integer type representing the number of elements stored in the vector...
static const std::decay< decltype(s_storage::max_size)>::type max_static_size
Maximum number of elements that can be stored in static storage.
void operator()(msgpack::packer< Stream > &packer, const small_vector< T, std::integral_constant< std::size_t, Size >> &v, msgpack_format f) const
Call operator.
#define PIRANHA_TT_CHECK(tt,...)
Macro for static type trait checks.
value_type & operator[](const size_type &n)
Subscript operator.
std::tuple< size_type, iterator, iterator > size_begin_end()
Size, begin and end.
void sub(small_vector &retval, const small_vector &other) const
Vector subtraction.
Low-level memory management functions.
void add(small_vector &retval, const small_vector &other) const
Vector addition.