diff --git a/tstl/include/small_vector.hpp b/tstl/include/small_vector.hpp new file mode 100644 index 00000000..8954c771 --- /dev/null +++ b/tstl/include/small_vector.hpp @@ -0,0 +1,733 @@ +//======================================================================= +// Copyright Baptiste Wicht 2013-2018. +// Distributed under the terms of the MIT License. +// (See accompanying file LICENSE or copy at +// http://www.opensource.org/licenses/MIT) +//======================================================================= + +#ifndef SMALL_VECTOR_H +#define SMALL_VECTOR_H + +#include +#include +#include +#include +#include + +namespace std { + +template +struct vector_base_long { + size_t capacity; + T* data; + + vector_base_long() = default; + + vector_base_long(size_t capacity, T* array) : capacity(capacity), data(array) {} + + vector_base_long(vector_base_long&) = delete; + vector_base_long& operator=(vector_base_long&) = delete; + + vector_base_long(vector_base_long&&) = default; + vector_base_long& operator=(vector_base_long&&) = default; +}; + +static constexpr const size_t vector_min_capacity = 16; +static constexpr const size_t vector_words = vector_min_capacity / sizeof(size_t); + +template +struct vector_base_small { + static constexpr size_t min_capacity = vector_min_capacity / sizeof(T); + + T data[vector_min_capacity]; + + vector_base_small() = default; + + vector_base_small(vector_base_small&) = delete; + vector_base_small& operator=(vector_base_small&) = delete; + + vector_base_small(vector_base_small&&) = default; + vector_base_small& operator=(vector_base_small&&) = default; +}; + +struct vector_base_raw { + size_t data[vector_words]; + + vector_base_raw() = delete; + + vector_base_raw(vector_base_raw&) = delete; + vector_base_raw& operator=(vector_base_raw&) = delete; + + vector_base_raw(vector_base_raw&&) = delete; + vector_base_raw& operator=(vector_base_raw&&) = delete; +}; + +template +union vector_base_storage { + vector_base_small small; + vector_base_long big; + vector_base_raw raw; + + vector_base_storage(){ + //Default construction: Nothing to do + } + + ~vector_base_storage() {} +}; + +static_assert(vector_min_capacity == sizeof(vector_base_small), "vector_base_small must be the correct SSO size"); +static_assert(vector_min_capacity == sizeof(vector_base_long), "vector_base_long must be the correct SSO size"); +static_assert(vector_min_capacity == sizeof(vector_base_raw), "vector_base_raw must be the correct SSO size"); + +/*! + * \brief A string of the given character type. + * + * This implementation uses SSO to not allocate any dynamic memory on short strings (<16 chars) + */ +template +struct small_vector { + using value_type = T; ///< The value type contained in the vector + using pointer_type = value_type*; ///< The pointer type contained in the vector + using reference_type = value_type&; ///< The pointer type contained in the vector + using const_reference_type = const value_type&; ///< The pointer type contained in the vector + using size_type = size_t; ///< The size type + using iterator = value_type*; ///< The iterator type + using const_iterator = const value_type*; ///< The const iterator type + + using reverse_iterator = std::reverse_iterator; ///< The reverse iterator type + using const_reverse_iterator = std::reverse_iterator; ///< The const reverse iterator type + + static constexpr size_t small_capacity = vector_min_capacity / sizeof(T); + +private: + size_t _size; + + vector_base_storage storage; + + void set_long(bool small){ + if(small){ + _size |= (1UL << 63); + } else { + _size &= ~(1UL << 63); + } + } + + void set_small(bool small){ + set_long(!small); + } + + void set_size(size_t size){ + if(is_long()){ + _size = size | (1UL << 63); + } else { + _size = size; + } + } + + bool is_long() const { + return _size & (1UL << 63); + } + + bool is_small() const { + return !is_long(); + } + + void zero(){ + for(size_t i = 0; i < vector_words; ++i){ + storage.raw.data[i] = 0; + } + } + +public: + //Constructors + + /*! + * \brief Construct an empty vector + */ + small_vector() : _size(0){ + set_small(true); + } + + /*! + * \brief Construct a new vector of the given capacity. + */ + explicit small_vector(size_t __size, T value = T()) : _size(__size) { + set_small(__size <= small_capacity); + + if(!is_small()){ + new (&storage.big) vector_base_long(__size, allocate(__size)); + } + + // Fill with the default value + for(size_t i = 0; i < __size; ++i){ + new (&data_ptr()[i]) value_type(value); + } + } + + /*! + * \brief Construct a vector containing the given values + */ + small_vector(initializer_list values) : _size(values.size()) { + set_small(values.size() <= small_capacity); + + if(!is_small()){ + new (&storage.big) vector_base_long(size(), allocate(size())); + } + + size_t i = 0; + for(auto& v : values){ + new (&data_ptr()[i++]) value_type(v); + } + } + + /*! + * \brief Construct a new string from the given range of characters + */ + template + small_vector(It it, It end) { + _size = std::distance(it, end); + + auto capacity = size() + 1; + + set_small(capacity <= small_capacity); + + if(!is_small()){ + new (&storage.big) vector_base_long(capacity, allocate(capacity)); + } + + auto oit = begin(); + while(it != end){ + *oit++ = *it++; + } + + (*this)[size()] = '\0'; + } + + //Copy + + small_vector(const small_vector& rhs) : _size(rhs._size) { + if(!is_small()){ + new (&storage.big) vector_base_long(size() + 1, allocate(size() + 1)); + } + + // Copy the new values + for(size_t i = 0; i < size(); ++i){ + new (&data_ptr()[i]) value_type(rhs.data_ptr()[i]); + } + } + + small_vector& operator=(const small_vector& rhs){ + if(this != &rhs){ + set_size(rhs.size()); + + // We need to destruct the previous element + release(); + + if(capacity() < rhs.capacity()){ + auto capacity = rhs.capacity(); + + if(is_small()){ + new (&storage.big) vector_base_long(capacity, allocate(capacity)); + + set_small(false); + } else { + storage.big.capacity = capacity; + storage.big.data = allocate(capacity); + } + } + + // Copy the new values + for (size_t i = 0; i < size(); ++i) { + new (&data_ptr()[i]) value_type(rhs.data_ptr()[i]); + } + } + + return *this; + } + + //Move + + small_vector(small_vector&& rhs) : _size(rhs._size) { + if(is_small()){ + new (&storage.small) vector_base_small(std::move(rhs.storage.small)); + } else { + new (&storage.big) vector_base_long(std::move(rhs.storage.big)); + } + + rhs.set_size(0); + rhs.zero(); + } + + small_vector& operator=(small_vector&& rhs){ + auto was_small = is_small(); + auto was_long = !was_small; + + auto small = rhs.is_small(); + auto lng = !small; + + set_size(rhs.size()); + + if(was_small && small){ + storage.small = std::move(rhs.storage.small); + } else if(was_long && lng){ + storage.big = std::move(rhs.storage.big); + } else if(was_small && lng){ + new (&storage.big) vector_base_long(std::move(rhs.storage.big)); + + set_small(false); + } else if(was_long && small){ + ensure_capacity(rhs.size() + 1); + + std::move_n(rhs.begin(), size() + 1, begin()); + } + + rhs.set_size(0); + rhs.zero(); + + return *this; + } + + //Destructors + + /*! + * \brief Destructs the string and releases all its associated memory + */ + ~small_vector(){ + release(); + } + + //Modifiers + + void adjust_size(size_t size){ + set_size(size); + } + + /*! + * \brief Clear the string + */ + void clear(){ + destruct_all(); + + set_size(0); + } + + /*! + * \brief Pop the last character of the string. + */ + void pop_back(){ + set_size(size() - 1); + + // Call the destructor of the erased value + data_ptr()[size()].~value_type(); + } + + /*! + * \brief Ensures a capacity of at least new_capacity + */ + void reserve(size_t new_capacity){ + ensure_capacity(new_capacity); + } + + /*! + * \brief Resize the vector to the given size + */ + void resize(size_t new_size){ + if(new_size > size()){ + ensure_capacity(new_size); + + // Default initialize the new elements + for(size_t i = size(); i < new_size; ++i){ + new (&data_ptr()[i]) value_type(); + } + + set_size(new_size); + } else if(new_size < size()){ + // Call the necessary destructors + for(size_t i = new_size; i < size(); ++i){ + data_ptr()[i].~value_type(); + } + + //By diminishing the size, the last elements become unreachable + set_size(new_size); + } + } + + /*! + * \brief Add an element at the back of the vector + */ + void push_back(value_type&& element){ + const auto old_size = size(); + + ensure_capacity(old_size + 1); + + new (&data_ptr()[old_size]) value_type(std::move(element)); + + set_size(old_size + 1); + } + + /*! + * \brief Add an element at the back of the vector + */ + void push_back(const value_type& element){ + const auto old_size = size(); + + ensure_capacity(old_size + 1); + + new (&data_ptr()[old_size]) value_type(element); + + set_size(old_size + 1); + } + + /*! + * \brief Construct a new element inplace + */ + value_type& emplace_back(){ + const auto old_size = size(); + + ensure_capacity(old_size + 1); + + new (&data_ptr()[old_size]) value_type(); + + set_size(old_size + 1); + + return back(); + } + + /*! + * \brief Construct a new element inplace + */ + template + value_type& emplace_back(Args... args){ + const auto old_size = size(); + + ensure_capacity(old_size + 1); + + new (&data_ptr()[old_size]) value_type{std::forward(args)...}; + + set_size(old_size + 1); + + return back(); + } + + /*! + * \brief Add an element at the front of the vector + */ + void push_front(value_type&& element){ + const auto old_size = size(); + + ensure_capacity(old_size + 1); + + if(!empty()){ + new (&data_ptr()[old_size]) value_type(std::move(data_ptr()[old_size - 1])); + + for (size_t i = old_size - 1; i > 0; --i) { + data_ptr()[i] = std::move(data_ptr()[i - 1]); + } + } + + // At this point _data[0] has been deleted + data_ptr()[0] = std::move(element); + + set_size(old_size + 1); + } + + /*! + * \brief Add an element at the front of the vector + */ + void push_front(const value_type& element){ + const auto old_size = size(); + + ensure_capacity(old_size + 1); + + if(!empty()){ + new (&data_ptr()[old_size]) value_type(std::move(data_ptr()[old_size - 1])); + + for (size_t i = old_size - 1; i > 0; --i) { + data_ptr()[i] = std::move(data_ptr()[i - 1]); + } + } + + // At this point _data[0] has been deleted + data_ptr()[0] = element; + + set_size(old_size + 1); + } + + /*! + * \brief Erase the element at the given position + */ + void erase(size_t position){ + for(size_t i = position; i < size() - 1; ++i){ + data_ptr()[i] = std::move(data_ptr()[i+1]); + } + + set_size(size() - 1); + + // Call the destructor of the last value + data_ptr()[size()].~value_type(); + } + + /*! + * \brief Erase the element at the given position + */ + void erase(iterator position){ + for(size_t i = position - begin(); i < size() - 1; ++i){ + data_ptr()[i] = std::move(data_ptr()[i+1]); + } + + set_size(size() - 1); + + // Call the destructor of the last value + data_ptr()[size()].~value_type(); + } + + /*! + * \brief Erase all the elements of the given range + */ + void erase(iterator first, iterator last){ + auto n = std::distance(first, last); + + for(size_t i = first - begin(); i < size() - n; ++i){ + data_ptr()[i] = std::move(data_ptr()[i+n]); + } + + // Call the destructors on the erase elements + for(size_t i = size() - n; i < size(); ++i){ + data_ptr()[i].~value_type(); + } + + set_size(size() - n); + } + + //Accessors + + /*! + * \brief Returns the size of the string + */ + size_t size() const { + return _size & ~(1UL << 63); + } + + /*! + * \brief Returns the capacity of the string + */ + size_t capacity() const { + if(is_small()){ + return small_capacity; + } else { + return storage.big.capacity; + } + } + + /* + * \brief Indicates if the string is empty + */ + bool empty() const { + return size() == 0; + } + + T* data_ptr(){ + if(is_small()){ + return &storage.small.data[0]; + } else { + return &storage.big.data[0]; + } + } + + const T* data_ptr() const { + if(is_small()){ + return &storage.small.data[0]; + } else { + return &storage.big.data[0]; + } + } + + /*! + * \brief Returns a reference to the ith character + */ + T& operator[](size_t i){ + return data_ptr()[i]; + } + + /*! + * \brief Returns a const reference to the ith character + */ + const T& operator[](size_t i) const { + return data_ptr()[i]; + } + + /*! + * \brief Returns a reference to the element at the front of the collection + */ + value_type& front(){ + return data_ptr()[0]; + } + + /*! + * \brief Returns a const reference to the element at the front of the collection + */ + const value_type& front() const { + return data_ptr()[0]; + } + + /*! + * \brief Returns a reference to the element at the back of the collection + */ + value_type& back(){ + return data_ptr()[size() - 1]; + } + + /*! + * \brief Returns a const reference to the element at the back of the collection + */ + const value_type& back() const { + return data_ptr()[size() - 1]; + } + + //Operators + + /*! + * \brief Test if this string is equal to the given string + */ + bool operator==(const small_vector& rhs) const { + if(size() != rhs.size()){ + return false; + } + + for(size_t i = 0; i < size(); ++i){ + if((*this)[i] != rhs[i]){ + return false; + } + } + + return true; + } + + /*! + * \brief Test if this string is not equal to the given string + */ + bool operator!=(const small_vector& rhs) const { + return !(*this == rhs); + } + + //Iterators + + /*! + * \brief Returns an iterator to the first character of the string + */ + iterator begin(){ + return iterator(&data_ptr()[0]); + } + + /*! + * \brief Returns a const iterator to the first character of the string + */ + const_iterator begin() const { + return const_iterator(&data_ptr()[0]); + } + + /*! + * \brief Returns an iterator the past-the-end character of the string + */ + iterator end(){ + return iterator(&data_ptr()[size()]); + } + + /*! + * \brief Returns a const iterator the past-the-end character of the string + */ + const_iterator end() const { + return const_iterator(&data_ptr()[size()]); + } + + // Reverse Iterators + + /*! + * \brief Return a reverse iterator to point to the first element + */ + reverse_iterator rbegin(){ + return reverse_iterator(&data_ptr()[int64_t(size()) - 1]); + } + + /*! + * \brief Return a reverse iterator to point to the first element + */ + constexpr const_reverse_iterator rbegin() const { + return const_iterator(&data_ptr()[int64_t(size()) - 1]); + } + + /*! + * \brief Return a reverse iterator point to the past-the-end element + */ + reverse_iterator rend(){ + return reverse_iterator(&data_ptr()[-1]); + } + + /*! + * \brief Return a reverse iterator point to the past-the-end element + */ + constexpr const_reverse_iterator rend() const { + return const_reverse_iterator(&data_ptr()[-1]); + } + +private: + static value_type* allocate(size_t n){ + return reinterpret_cast(new uint8_t[n * sizeof(value_type)]); + } + + static void deallocate(value_type* ptr){ + delete[] reinterpret_cast(ptr); + } + + void destruct_all(){ + // Call the destructors + for(size_t i = 0; i < size(); ++i){ + data_ptr()[i].~value_type(); + } + } + + void release(){ + destruct_all(); + + if(!is_small()){ + // Deallocate the memory + deallocate(storage.big.data); + storage.big.data = nullptr; + } + } + + void ensure_capacity(size_t new_capacity){ + // Note: We know that min capacity is constant, therefore + // augmenting the capacity always means going to long + // storage + if(new_capacity > 0 && (capacity() < new_capacity)){ + auto new_cap = capacity() * 2; + + if(new_cap < new_capacity){ + new_cap = new_capacity; + } + + auto new_data = allocate(new_cap); + + // Move the old _data into the new one + for(size_t i = 0; i < size(); ++i){ + new (&new_data[i]) value_type(std::move(data_ptr()[i])); + } + + if(is_small()){ + new (&storage.big) vector_base_long(new_cap, new_data); + + set_small(false); + } else { + release(); + + storage.big.data = new_data; + storage.big.capacity = new_cap; + } + } + } +}; + +} //end of namespace std + +#endif