#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(pointer_iterator<T, 1> it) {
|
|
for(auto step = it + 1; step<end(); step++) {
|
|
(*it++) = gp::move(*step);
|
|
}
|
|
*rbegin().~T();
|
|
sz -= 1;
|
|
}
|
|
|
|
constexpr pointer_iterator<T, 1> begin()
|
|
{
|
|
return associated_iterator(&ary[0]);
|
|
}
|
|
|
|
constexpr pointer_iterator<T, 1> end()
|
|
{
|
|
return associated_iterator(&ary[sz]);
|
|
}
|
|
|
|
constexpr const_pointer_iterator<T, 1> cbegin() const
|
|
{
|
|
return associated_const_iterator(&ary[0]);
|
|
}
|
|
|
|
constexpr const_pointer_iterator<T, 1> cend() const
|
|
{
|
|
return associated_const_iterator(&ary[sz]);
|
|
}
|
|
|
|
constexpr pointer_iterator<T, -1> rbegin()
|
|
{
|
|
return associated_riterator(&ary[sz-1]);
|
|
}
|
|
|
|
constexpr pointer_iterator<T, -1> rend()
|
|
{
|
|
return associated_riterator(ary-1);
|
|
}
|
|
|
|
constexpr const_pointer_iterator<T, -1> crbegin() const
|
|
{
|
|
return associated_const_riterator(&ary[sz-1]);
|
|
}
|
|
|
|
constexpr 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};
|
|
}
|
|
};
|
|
}
|