|
|
- #pragma once
-
- #include <gp/containers/buffer.hpp>
- #include <gp/utils/allocators/allocator.hpp>
-
- #include <initializer_list>
-
- namespace gp{
- /**
- * @brief A vector type most similar to that of the standard library
- * Always uses polymorphic allocation, no small value optimization
- *
- * @tparam T
- */
- template<typename T>
- class vector final {
- T* ary = nullptr; //< The data
- size_t sz = 0; //< the used size of the array
- size_t cap = 0; //< the available capacity of the array
- gp::reference_wrapper<allocator> alloc; //< the allocator
- public:
- using associated_iterator = pointer_iterator<T, 1>;
- using associated_const_iterator = const_pointer_iterator<T, 1>;
- using associated_riterator = pointer_iterator<T, -1>;
- using associated_const_riterator = const_pointer_iterator<T, -1>;
-
- /**
- * @brief Construct a new vector object from an allocator
- *
- * @param v the allocator
- */
- vector(allocator& v)
- : ary()
- , alloc(v)
- {}
-
- /**
- * @brief Construct a new vector object from another vector, copying it
- *
- * @param oth the other vector
- */
- vector(vector& oth)
- : alloc(oth.alloc)
- {
- sz = 0;
- cap = 0;
- ary = nullptr;
- gp_config::assertion(reserve(oth.size()), "could not reserve space on building");
- sz = oth.size();
- cap = oth.size();
- auto it_l = begin();
- auto it_o = oth.cbegin();
- for(size_t i = 0; i < sz; ++i)
- {
- new(&*(it_l++)) T(*(it_o++));
- }
- }
-
- /**
- * @brief Construct a new vector object by moving objects out of another vector
- *
- * @param oth
- */
- vector(vector&& oth)
- : ary(oth.ary)
- , sz(oth.sz)
- , cap(oth.cap)
- , alloc(oth.alloc)
- {
- oth.sz = 0;
- oth.cap = 0;
- oth.ary = nullptr;
- }
-
- /**
- * @brief Copy assignment
- *
- * @param oth
- * @return vector& the reference to this changed vector
- */
- vector& operator=(vector& oth)
- {
- gp_config::assertion(reserve(oth.size()), "could not reserve space on assign");
- for(size_t i = 0; i < gp::min(sz, oth.sz); ++i)
- {
- if constexpr (!std::is_trivially_destructible_v<T>) ary[i]->~T();
- new(ary+i) T(oth[i]);
- }
- if(sz < oth.sz) {
- for(size_t i = sz; i < oth.sz; ++i) {
- if constexpr (!std::is_trivially_destructible_v<T>) ary[i]->~T();
- new(ary+i) T(oth[i]);
- }
- } else if(sz > oth.sz) {
- if constexpr (!std::is_trivially_destructible_v<T>)
- for(size_t i = oth.sz; i < sz; ++i) {
- ary[i]->~T();
- }
- }
- sz = oth.sz;
- return *this;
- }
-
- /**
- * @brief Move assignment
- *
- * Will not change the allocator of the local vector, is EXPENSIVE if the both vectors have different allocators
- *
- * @param oth
- * @return vector&
- */
- vector& operator=(vector&& oth)
- {
- if(&alloc.get() == &oth.alloc.get())
- {
- gp::swap(ary, oth.ary);
- gp::swap(sz, oth.sz);
- gp::swap(cap, oth.cap);
- } else {
- for(auto& elem : *this) {
- elem->~T();
- }
- sz = 0;
- if(capacity()<oth.size()) {
- reserve(oth.size());
- }
- size_t idx = 0;
- for(auto& elem : oth) {
- new(ary+idx) T(gp::move(elem));
- ++idx;
- elem.~T();
- }
- gp_config::assertion(alloc.get().deallocate(oth.ary), "could not deallocate");
- sz = idx;
- oth.sz = 0;
- oth.capacity = 0;
- oth.ary = nullptr;
- }
- return *this;
- }
-
- constexpr T& operator[] (size_t off)
- {
- if constexpr (gp_config::has_buffer_bounds)
- {
- gp_config::assertion(
- off < sz,
- "Array bounds infringed"
- );
- }
- return ary[off];
- }
-
- ~vector()
- {
- if(ary)
- {
- if constexpr (!std::is_trivially_destructible_v<T>)
- for(auto& elem : *this) {
- elem.~T();
- }
- gp_config::assertion(alloc.get().deallocate(ary), "could not deallocate");
- ary = nullptr;
- }
- }
-
- /**
- * @brief Ensures the vector can hold at least 1 more element
- *
- * @return true if it is at least now possible to fit one more element
- * @return false if fiting one more element is not possible even now
- */
- bool grow() {
- if(sz == cap) return reserve(1 + sz + (sz >> 1));
- return true;
- }
-
- /**
- * @brief Reserves space so that the capacity is at least equal to the provided value.
- *
- * This will never shrink the datastructure
- *
- * @param new_cap the new capacity
- * @return true on success
- * @return false on failure
- */
- bool reserve(size_t new_cap) {
- if(new_cap <= cap) return true;
-
- size_t new_data_size = new_cap*sizeof(T);
-
- if(alloc.get().try_reallocate(ary, new_data_size)) return true;
-
- if(T* new_ary = (T*)alloc.get().allocate(new_data_size); new_ary) {
- auto new_it = new_ary;
- for(auto& elem : *this) {
- new(new_it++) T(gp::move(elem));
- }
-
- if(ary != nullptr) gp_config::assertion(alloc.get().deallocate(ary), "failed to deallocate old range");
-
- ary = new_ary;
- cap = new_cap;
-
- return true;
- }
-
- return false;
- }
-
- constexpr const T& operator[] (size_t off) const
- {
- if constexpr (gp_config::has_buffer_bounds)
- {
- gp_config::assertion(
- off < sz,
- "Array bounds infringed"
- );
- }
- return ary[off];
- }
-
- constexpr size_t size() const
- {
- return sz;
- }
-
- constexpr size_t capacity() const
- {
- return cap;
- }
-
- /**
- * @brief Adds the provided value to the vector
- *
- * @param value
- * @return true on success
- * @return false on failure
- */
- constexpr bool push_back(T& value) {
- if(grow()) {
- new(ary+sz) T(value);
- sz++;
- return true;
- }
- return false;
- }
-
-
- /**
- * @brief Moves the provided value to the vector
- *
- * @param value
- * @return true on success
- * @return false on failure
- */
- constexpr bool push_back(T&& value) {
- if(grow()) {
- new(ary+sz) T(gp::move(value));
- sz++;
- return true;
- }
- return false;
- }
-
- /**
- * @brief Constructs a new element at the end of the vector
- *
- * @param value the parameters to be sent to the constructor of T
- * @return true on success
- * @return false on failure
- */
- template<typename ...U>
- constexpr bool emplace_back(U&&... value) {
- if(grow()) {
- new(ary+sz) T(gp::forward<U>(value)...);
- sz++;
- return true;
- }
- return false;
- }
-
- /**
- * @brief moves the last element of the vector out if it exists, returning an optional.
- *
- * @return constexpr gp::optional<T> contains a value if it existed, else it is empty
- */
- constexpr gp::optional<T> pop_back() {
- if(sz == 0) return gp::nullopt;
- sz--;
- gp::optional<T> ret_val = gp::move(ary[sz]);
- ary[sz]->~T();
- return gp::move(ret_val);
- }
-
- void remove(gp::pointer_iterator<T, 1> it) {
- for(auto step = it + 1; step!=end(); step++) {
- (*it++) = gp::move(*step);
- }
- (*rbegin()).~T();
- sz -= 1;
- }
-
- constexpr gp::pointer_iterator<T, 1> begin()
- {
- return associated_iterator(&ary[0]);
- }
-
- constexpr gp::pointer_iterator<T, 1> end()
- {
- return associated_iterator(&ary[sz]);
- }
-
- constexpr gp::const_pointer_iterator<T, 1> cbegin() const
- {
- return associated_const_iterator(&ary[0]);
- }
-
- constexpr gp::const_pointer_iterator<T, 1> cend() const
- {
- return associated_const_iterator(&ary[sz]);
- }
-
- constexpr gp::pointer_iterator<T, -1> rbegin()
- {
- return associated_riterator(&ary[sz-1]);
- }
-
- constexpr gp::pointer_iterator<T, -1> rend()
- {
- return associated_riterator(ary-1);
- }
-
- constexpr gp::const_pointer_iterator<T, -1> crbegin() const
- {
- return associated_const_riterator(&ary[sz-1]);
- }
-
- constexpr gp::const_pointer_iterator<T, -1> crend() const
- {
- return associated_const_riterator(ary-1);
- }
-
- constexpr bool operator==(const vector& oth) const
- {
- for(size_t idx = 0; idx<sz; idx++)
- {
- if(ary[idx] != oth.ary[idx])
- {
- return false;
- }
- }
- return true;
- }
-
- constexpr bool operator!=(const vector& oth) const
- {
- return !(*this == oth);
- }
-
- /**
- * @brief Provides a span access to the vector
- *
- * @return A buffer of the vector.
- * It is invalidated by any operation that may change the size of the vector.
- */
- gp::buffer<T> as_buffer()
- {
- return gp::buffer<T>{(T*)ary, (T*)ary+sz};
- }
- };
- }
|