/* Self-expanding dynamic array implementation for OctaSTD. * * This file is part of OctaSTD. See COPYING.md for futher information. */ #ifndef OCTA_VECTOR_H #define OCTA_VECTOR_H #include #include #include "octa/new.h" #include "octa/type_traits.h" #include "octa/utility.h" #include "octa/range.h" #include "octa/algorithm.h" #include "octa/initializer_list.h" namespace octa { template class Vector { T *p_buf; size_t p_len, p_cap; void insert_base(size_t idx, size_t n) noexcept( IsNothrowDestructible::value && IsNothrowMoveConstructible::value && IsNothrowMoveAssignable::value ) { if (p_len + n > p_cap) reserve(p_len + n); p_len += n; for (size_t i = p_len - 1; i > idx + n - 1; --i) { p_buf[i] = move(p_buf[i - n]); } } public: enum { MIN_SIZE = 8 }; typedef size_t SizeType; typedef ptrdiff_t DiffType; typedef T ValType; typedef T &RefType; typedef const T &ConstRefType; typedef T *PtrType; typedef const T *ConstPtrType; typedef PointerRange< T> RangeType; typedef PointerRange ConstRangeType; Vector() noexcept: p_buf(nullptr), p_len(0), p_cap(0) {} explicit Vector(size_t n, const T &val = T()) noexcept( IsNothrowCopyConstructible::value ): Vector() { p_buf = new uchar[n * sizeof(T)]; p_len = p_cap = n; T *cur = p_buf, *last = p_buf + n; while (cur != last) new (cur++) T(val); } Vector(const Vector &v) noexcept( IsNothrowDestructible::value && IsNothrowCopyConstructible::value ): Vector() { *this = v; } Vector(Vector &&v) noexcept: p_buf(v.p_buf), p_len(v.p_len), p_cap(v.p_cap) { v.p_buf = nullptr; v.p_len = v.p_cap = 0; } Vector(InitializerList v) noexcept( IsNothrowCopyConstructible::value ): Vector() { size_t len = v.length(); const T *ptr = v.get(); reserve(len); for (size_t i = 0; i < len; ++i) new (&p_buf[i]) T(ptr[i]); p_len = len; } ~Vector() noexcept(IsNothrowDestructible::value) { clear(); delete[] (uchar *)p_buf; p_buf = nullptr; p_cap = 0; } void clear() noexcept(IsNothrowDestructible::value) { if (p_len > 0 && !octa::IsPod()) { T *cur = p_buf, *last = p_buf + p_len; while (cur != last) (*cur++).~T(); } p_len = 0; } Vector &operator=(const Vector &v) noexcept( IsNothrowDestructible::value && IsNothrowCopyConstructible::value ) { if (this == &v) return *this; clear(); reserve(v.p_cap); p_len = v.p_len; if (octa::IsPod()) { memcpy(p_buf, v.p_buf, p_len * sizeof(T)); } else { T *cur = p_buf, *last = p_buf + p_len; T *vbuf = v.p_buf; while (cur != last) { new (cur++) T(*vbuf++); } } return *this; } Vector &operator=(Vector &&v) noexcept( IsNothrowDestructible::value ) { clear(); delete[] (uchar *)p_buf; p_len = v.p_len; p_cap = v.p_cap; p_buf = v.disown(); return *this; } Vector &operator=(InitializerList il) noexcept( IsNothrowDestructible::value && IsNothrowCopyConstructible::value ) { clear(); size_t ilen = il.length(); reserve(ilen); if (octa::IsPod()) { memcpy(p_buf, il.get(), ilen); } else { T *buf = p_buf, *ibuf = il.get(), *last = il.get() + ilen; while (ibuf != last) { new (buf++) T(*ibuf++); } } p_len = ilen; return *this; } void resize(size_t n, const T &v = T()) noexcept( IsNothrowDestructible::value && IsNothrowMoveConstructible::value && IsNothrowCopyConstructible::value ) { size_t len = p_len; reserve(n); p_len = n; if (octa::IsPod()) { for (size_t i = len; i < p_len; ++i) { p_buf[i] = T(v); } } else { T *first = p_buf + len; T *last = p_buf + p_len; while (first != last) new (first++) T(v); } } void reserve(size_t n) noexcept( IsNothrowDestructible::value && IsNothrowMoveConstructible::value ) { if (n <= p_len) { if (n == p_len) return; while (p_len > n) pop(); return; } size_t oc = p_cap; if (!oc) { p_cap = max(n, size_t(MIN_SIZE)); } else { while (p_cap < n) p_cap *= 2; } T *tmp = (T *)new uchar[p_cap * sizeof(T)]; if (oc > 0) { if (octa::IsPod()) { memcpy(tmp, p_buf, p_len * sizeof(T)); } else { T *cur = p_buf, *tcur = tmp, *last = tmp + p_len; while (tcur != last) { new (tcur++) T(move(*cur)); (*cur).~T(); ++cur; } } delete[] (uchar *)p_buf; } p_buf = tmp; } T &operator[](size_t i) noexcept { return p_buf[i]; } const T &operator[](size_t i) const noexcept { return p_buf[i]; } T &at(size_t i) noexcept { return p_buf[i]; } const T &at(size_t i) const noexcept { return p_buf[i]; } T &push(const T &v) noexcept( noexcept(reserve(p_len + 1)) && IsNothrowCopyConstructible::value ) { if (p_len == p_cap) reserve(p_len + 1); new (&p_buf[p_len]) T(v); return p_buf[p_len++]; } T &push() noexcept( noexcept(reserve(p_len + 1)) && IsNothrowDefaultConstructible::value ) { if (p_len == p_cap) reserve(p_len + 1); new (&p_buf[p_len]) T; return p_buf[p_len++]; } template T &emplace_back(U &&...args) { if (p_len == p_cap) reserve(p_len + 1); new (&p_buf[p_len]) T(forward(args)...); return p_buf[p_len++]; } void pop() noexcept(IsNothrowDestructible::value) { if (!octa::IsPod()) { p_buf[--p_len].~T(); } else { --p_len; } } T &pop_ret() noexcept { return p_buf[--p_len]; } T &first() noexcept { return p_buf[0]; } const T &first() const noexcept { return p_buf[0]; }; T &last() noexcept { return p_buf[p_len - 1]; } const T &last() const noexcept { return p_buf[p_len - 1]; } T *get() noexcept { return p_buf; } const T *get() const noexcept { return p_buf; } size_t length() const noexcept { return p_len; } size_t capacity() const noexcept { return p_cap; } bool empty() const noexcept { return (p_len == 0); } bool in_range(size_t idx) noexcept { return idx < p_len; } bool in_range(int idx) noexcept { return idx >= 0 && idx < p_len; } bool in_range(const T *ptr) noexcept { return ptr >= p_buf && ptr < &p_buf[p_len]; } T *disown() noexcept { T *r = p_buf; p_buf = nullptr; p_len = p_cap = 0; return r; } T *insert(size_t idx, T &&v) noexcept( IsNothrowDestructible::value && IsNothrowMoveConstructible::value && IsNothrowMoveAssignable::value ) { insert_base(idx, 1); p_buf[idx] = move(v); return &p_buf[idx]; } T *insert(size_t idx, const T &v) noexcept( IsNothrowDestructible::value && IsNothrowMoveConstructible::value && IsNothrowMoveAssignable::value && IsNothrowCopyAssignable::value ) { insert_base(idx, 1); p_buf[idx] = v; return &p_buf[idx]; } T *insert(size_t idx, size_t n, const T &v) noexcept( IsNothrowDestructible::value && IsNothrowMoveConstructible::value && IsNothrowMoveAssignable::value && IsNothrowCopyAssignable::value ) { insert_base(idx, n); for (size_t i = 0; i < n; ++i) { p_buf[idx + i] = v; } return &p_buf[idx]; } template T *insert_range(size_t idx, U range) noexcept( IsNothrowDestructible::value && IsNothrowMoveConstructible::value && IsNothrowMoveAssignable::value && noexcept(range.first()) && noexcept(range.pop_first()) && noexcept((*p_buf = range.first())) ) { size_t len = range.length(); insert_base(idx, len); for (size_t i = 0; i < len; ++i) { p_buf[idx + i] = range.first(); range.pop_first(); } return &p_buf[idx]; } T *insert(size_t idx, InitializerList il) noexcept( noexcept(declval>().insert_range(idx, il.range())) ) { return insert_range(idx, il.range()); } RangeType each() noexcept { return PointerRange(p_buf, p_buf + p_len); } ConstRangeType each() const noexcept { return PointerRange(p_buf, p_buf + p_len); } void swap(Vector &v) noexcept { swap(p_len, v.p_len); swap(p_cap, v.p_cap); swap(p_buf, v.p_buf); } }; template void swap(Vector &a, Vector &b) noexcept { a.swap(b); } } #endif