- Documented and Debugged containers
- Attempted to setup gdb prettywriters
This commit is contained in:
@@ -45,7 +45,7 @@ namespace fennec
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |:--------:|:-----------------------:|
|
||||
/// | stable | ✔ |
|
||||
/// | stable | \emoji heavy_check_mark |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
|
||||
@@ -42,21 +42,27 @@ namespace fennec
|
||||
///
|
||||
/// \brief wrapper for dynamically sized arrays
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |-----------|--------------|
|
||||
/// | stable | \emoji anger |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(N)\f$ |
|
||||
/// | deletion | \f$O(N)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// | Property | Value |
|
||||
/// |-----------|:----------:|
|
||||
/// | stable | \emoji x |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(N)\f$ |
|
||||
/// | deletion | \f$O(N)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT value type
|
||||
template<class TypeT, class Alloc = allocator<TypeT>>
|
||||
class dynarray {
|
||||
public:
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
|
||||
using element_t = TypeT;
|
||||
using alloc_t = Alloc;
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty allocation.
|
||||
constexpr dynarray() : _alloc(8), _size(0) {}
|
||||
@@ -67,11 +73,18 @@ public:
|
||||
constexpr dynarray(const alloc_t& alloc)
|
||||
: _alloc(8, alloc)
|
||||
, _size(0) {
|
||||
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Allocation, create an allocation with a size of `n` elements, initialized with the default constructor.
|
||||
/// \brief Alloc Move Constructor, initialize empty allocation with allocator instance.
|
||||
/// \param alloc An allocator object to copy, for instances where the allocator needs to be initialized with some data.
|
||||
constexpr dynarray(alloc_t&& alloc) noexcept
|
||||
: _alloc(8, alloc)
|
||||
, _size(0) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Allocation, create an allocation of size `n` elements, initialized with the default constructor.
|
||||
constexpr dynarray(size_t n)
|
||||
: _alloc(n)
|
||||
, _size(n)
|
||||
@@ -83,9 +96,9 @@ public:
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief
|
||||
/// \param n
|
||||
/// \param alloc
|
||||
/// \brief Sized Allocation Alloc Constructor, initializes a dynarray with allocator `alloc` and `n` elements using the default constructor.
|
||||
/// \param n The number of elements
|
||||
/// \param alloc The allocator object to copy
|
||||
constexpr dynarray(size_t n, const alloc_t& alloc)
|
||||
: _alloc(n, alloc)
|
||||
, _size(n) {
|
||||
@@ -96,7 +109,20 @@ public:
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Create an allocation of size `n`, with each element constructed using the copy constructor
|
||||
/// \brief Sized Allocation Alloc Move Constructor, initializes a dynarray with allocator `alloc` and `n` elements using the default constructor.
|
||||
/// \param n The number of elements
|
||||
/// \param alloc The allocator object to copy
|
||||
constexpr dynarray(size_t n, alloc_t&& alloc)
|
||||
: _alloc(n, alloc)
|
||||
, _size(n) {
|
||||
element_t* addr = _alloc.data();
|
||||
for(; n > 0; --n, ++addr) {
|
||||
fennec::construct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Sized Allocation Copy Constructor, Create an allocation of size `n` elements, with each element constructed using the copy constructor
|
||||
/// \brief n the number of elements
|
||||
constexpr dynarray(size_t n, const TypeT& val)
|
||||
: _alloc(n)
|
||||
@@ -107,8 +133,15 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
// This constructor should not be invokable since moving is a single object operation and will cause undefined
|
||||
// behaviour when moving to multiple elements
|
||||
constexpr dynarray(size_t n, TypeT&&) = delete;
|
||||
|
||||
///
|
||||
/// \brief Emplace Constructor
|
||||
/// \tparam ArgsT A sequence of argument types
|
||||
/// \param n The number of objects to create
|
||||
/// \param args The arguments to create each object with
|
||||
template<typename...ArgsT>
|
||||
constexpr dynarray(size_t n, ArgsT&&...args) {
|
||||
element_t* addr = _alloc.data();
|
||||
@@ -117,49 +150,109 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Default Destructor, destructs all elements and frees the underlying allocation
|
||||
constexpr ~dynarray() {
|
||||
element_t* addr = _alloc.data();
|
||||
if (addr == nullptr) return;
|
||||
for(int n = _size; n > 0; --n, ++addr) {
|
||||
fennec::destruct(addr);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \returns The size of the dynarray in elements
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns The current capacity, in elements, of the underlying allocation
|
||||
constexpr size_t capacity() const {
|
||||
return _alloc.capacity();
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns True when there are no elements active, otherwise false
|
||||
constexpr bool empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
|
||||
// Element Access ======================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator
|
||||
/// \param i The index to access
|
||||
/// \returns A reference to the element at index `i`
|
||||
constexpr TypeT& operator[](int i) {
|
||||
assertd(i >= 0 and size_t(i) < _size, "Array Out of Bounds");
|
||||
return _alloc.data()[i];
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator (const)
|
||||
/// \param i The index to access
|
||||
/// \returns A const qualified reference to the element at index `i`
|
||||
constexpr const TypeT& operator[](int i) const {
|
||||
assertd(i >= 0 and size_t(i) < _size, "Array Out of Bounds");
|
||||
return _alloc.data()[i];
|
||||
}
|
||||
|
||||
constexpr TypeT* begin() { return _alloc.data(); }
|
||||
constexpr TypeT* end() { return begin() + _size; }
|
||||
///
|
||||
/// \returns Reference to the first element in the dynarray
|
||||
constexpr TypeT& front() {
|
||||
return this->operator[](0);
|
||||
}
|
||||
|
||||
constexpr const TypeT* begin() const { return _alloc; }
|
||||
constexpr const TypeT* end() const { return begin() + _size; }
|
||||
///
|
||||
/// \returns A const-qualified reference to the first element in the dynarray
|
||||
constexpr const TypeT& front() const {
|
||||
return this->operator[](0);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A reference to the last element in the dynarray
|
||||
constexpr TypeT& back() {
|
||||
return this->operator[](size() - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const-qualified reference to the last element in the dynarray
|
||||
constexpr const TypeT& back() const {
|
||||
return this->operator[](size() - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief "Iterator" Begin Function
|
||||
/// \returns A pointer to the first element in the dynarray
|
||||
constexpr TypeT* begin() { return _alloc.data(); }
|
||||
|
||||
///
|
||||
/// \brief "Iterator" End Function
|
||||
/// \return A pointer to the address after the last element in the dynarray
|
||||
constexpr TypeT* end() { return begin() + _size; }
|
||||
|
||||
///
|
||||
/// \brief Const "Iterator" Begin Function
|
||||
/// \returns A const qualified pointer to the first element in the dynarray
|
||||
constexpr const TypeT* begin() const { return _alloc; }
|
||||
|
||||
///
|
||||
/// \brief Const "Iterator" End Function
|
||||
/// \return A const qualified pointer to the address after the last element in the dynarray
|
||||
constexpr const TypeT* end() const { return begin() + _size; }
|
||||
|
||||
|
||||
// Insertion & Deletion ================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param i index to insert at
|
||||
/// \param val the value to initialize with
|
||||
constexpr void insert(size_t i, TypeT&& val) {
|
||||
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
@@ -180,6 +273,10 @@ public:
|
||||
++_size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param i index to insert at
|
||||
/// \param val the value to initialize with
|
||||
constexpr void insert(size_t i, const TypeT& val) {
|
||||
|
||||
// Grow if the size has reached the capacity of the allocation
|
||||
@@ -190,9 +287,10 @@ public:
|
||||
// Move the data if we are not inserting at the end of the array
|
||||
if((i = min(i, _size)) < _size) {
|
||||
fennec::memmove(
|
||||
(void*)(_alloc.data() + i)
|
||||
, (void*)(_alloc.data() + i + 1)
|
||||
, (_size - i) * sizeof(TypeT));
|
||||
(void*)(_alloc.data() + i),
|
||||
(void*)(_alloc.data() + i + 1),
|
||||
(_size - i) * sizeof(TypeT)
|
||||
);
|
||||
}
|
||||
|
||||
// Insert the element
|
||||
@@ -200,6 +298,11 @@ public:
|
||||
++_size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \param i index to insert at
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(size_t i, ArgsT&&...args) {
|
||||
|
||||
@@ -221,31 +324,41 @@ public:
|
||||
++_size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Back Copy
|
||||
/// \param val Value to initialize with
|
||||
constexpr void push_back(const TypeT& val) {
|
||||
dynarray::insert(_size, val);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Back Move
|
||||
/// \param val Value to initialize with
|
||||
constexpr void push_back(TypeT&& val) {
|
||||
dynarray::insert(_size, fennec::forward<TypeT>(val));
|
||||
}
|
||||
|
||||
constexpr void pop_back() {
|
||||
fennec::destruct(&_alloc[_size--]);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Back
|
||||
/// \tparam ArgsT Argument Types
|
||||
/// \param args Arguments to construct with
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace_back(ArgsT...args) {
|
||||
dynarray::emplace(_size, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase last element
|
||||
constexpr void pop_back() {
|
||||
fennec::destruct(&_alloc[--_size]);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Resize the dynarray, invoking the default constructor for all new elements
|
||||
/// \param n The new size in elements
|
||||
constexpr void resize(size_t n) {
|
||||
_alloc.reallocate(n);
|
||||
|
||||
if (_size < n) {
|
||||
_size = n;
|
||||
return;
|
||||
}
|
||||
|
||||
while (_size < n) {
|
||||
emplace_back();
|
||||
}
|
||||
|
||||
@@ -45,41 +45,76 @@ namespace fennec
|
||||
///
|
||||
/// \brief wrapper for lists of elements
|
||||
/// \details
|
||||
/// | Property | Value |
|
||||
/// |-----------|--------------|
|
||||
/// | stable | \emoji anger |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
/// This data-structure behaves like a linked list, but does not use pointers. Instead, it is in-array. This creates the
|
||||
/// following properties:
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:----------|:-------------------------------------:|
|
||||
/// | stable | \emoji x |
|
||||
/// | access | \f$O(N)\f$ or \f$O(1)\f$ (front/back) |
|
||||
/// | insertion | \f$O(N)\f$ or \f$O(1)\f$ (iterator) |
|
||||
/// | deletion | \f$O(N)\f$ or \f$O(1)\f$ (iterator) |
|
||||
/// | space | \f$O(N)\f$ |
|
||||
///
|
||||
/// \tparam TypeT value type
|
||||
template<class TypeT, class Alloc = allocator<TypeT>>
|
||||
struct list {
|
||||
public:
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
|
||||
using value_t = TypeT;
|
||||
static constexpr size_t npos = -1;
|
||||
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
// Definitions =========================================================================================================
|
||||
|
||||
private:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
using elem_t = node;
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
|
||||
using value_t = TypeT;
|
||||
using elem_t = node;
|
||||
static constexpr size_t npos = -1;
|
||||
|
||||
class iterator;
|
||||
class const_iterator;
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes an empty list.
|
||||
constexpr list()
|
||||
: _table(), _freed(), _root(npos), _last(npos), _size(0) {
|
||||
}
|
||||
|
||||
constexpr ~list() = default;
|
||||
///
|
||||
/// \brief Destructor, destructs all elements then releases the allocation.
|
||||
constexpr ~list() {
|
||||
for (size_t i = 0; i < capacity(); ++i) {
|
||||
_table[i].data = nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
constexpr bool size() const { return _size; }
|
||||
constexpr bool capacity() const { return _table.capacity(); }
|
||||
constexpr bool empty() const { return _root == npos; }
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \returns The size of the list in elements.
|
||||
constexpr size_t size() const { return _size; }
|
||||
|
||||
///
|
||||
/// \returns The capacity of the list in elements.
|
||||
constexpr size_t capacity() const { return _table.capacity(); }
|
||||
|
||||
///
|
||||
/// \returns `true` when the list is empty, `false` otherwise.
|
||||
constexpr bool empty() const { return _root == npos; }
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Array Access Operator
|
||||
/// \param i Index to access
|
||||
/// \returns A reference to the element at `i`
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
constexpr value_t& operator[](int i) {
|
||||
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
||||
size_t n = _walk(i);
|
||||
@@ -87,6 +122,12 @@ public:
|
||||
return *_table[n].data;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Array Access Operator
|
||||
/// \param i Index to access
|
||||
/// \returns A const-qualified reference to the element at `i`
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
constexpr const value_t& operator[](int i) const {
|
||||
assertd(i >= 0 && size_t(i) < _size, "Index out of Bounds");
|
||||
size_t n = _walk(i);
|
||||
@@ -94,216 +135,176 @@ public:
|
||||
return *_table[n].data;
|
||||
}
|
||||
|
||||
void insert(const iterator& it, const value_t& x) {
|
||||
if (size() == capacity()) {
|
||||
_expand();
|
||||
}
|
||||
|
||||
size_t n = it._n;
|
||||
size_t p = _next_free();
|
||||
fennec::construct(&_table[p].data, x);
|
||||
if (n == npos) {
|
||||
if (empty()) {
|
||||
_root = p;
|
||||
_table[p].next = npos;
|
||||
_table[p].prev = npos;
|
||||
} else {
|
||||
_table[p].prev = n;
|
||||
_table[p].next = npos;
|
||||
_last = n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_table[p].next = n;
|
||||
_table[p].prev = _prev(n);
|
||||
_table[n].prev = p;
|
||||
++_size;
|
||||
}
|
||||
|
||||
void insert(const iterator& it, value_t&& x) {
|
||||
if (size() == capacity()) {
|
||||
_expand();
|
||||
}
|
||||
|
||||
size_t n = it._n;
|
||||
size_t p = _next_free();
|
||||
fennec::construct(&_table[p].data, fennec::forward<value_t>(x));
|
||||
if (n == npos) {
|
||||
if (empty()) {
|
||||
_root = p;
|
||||
_table[p].next = npos;
|
||||
_table[p].prev = npos;
|
||||
} else {
|
||||
_table[p].prev = n;
|
||||
_table[p].next = npos;
|
||||
_last = n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_table[p].next = n;
|
||||
_table[p].prev = _prev(n);
|
||||
_table[n].prev = p;
|
||||
++_size;
|
||||
}
|
||||
|
||||
void insert(size_t i, const value_t& x) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
if (size() == capacity()) {
|
||||
_expand();
|
||||
}
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
size_t p = _next_free();
|
||||
fennec::construct(&_table[p].data, x);
|
||||
if (n == npos) {
|
||||
if (empty()) {
|
||||
_root = p;
|
||||
_table[p].next = npos;
|
||||
_table[p].prev = npos;
|
||||
} else {
|
||||
_table[p].prev = n;
|
||||
_table[p].next = npos;
|
||||
_last = n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_table[p].next = n;
|
||||
_table[p].prev = _prev(n);
|
||||
_table[n].prev = p;
|
||||
++_size;
|
||||
}
|
||||
|
||||
void insert(size_t i, value_t&& x) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
if (size() == capacity()) {
|
||||
_expand();
|
||||
}
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
size_t p = _next_free();
|
||||
fennec::construct(&_table[p].data, fennec::forward<value_t>(x));
|
||||
if (n == npos) {
|
||||
if (empty()) {
|
||||
_root = p;
|
||||
_table[p].next = npos;
|
||||
_table[p].prev = npos;
|
||||
} else {
|
||||
_table[p].prev = n;
|
||||
_table[p].next = npos;
|
||||
_last = n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_table[p].next = n;
|
||||
_table[p].prev = _prev(n);
|
||||
_table[n].prev = p;
|
||||
++_size;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
void emplace(size_t i, ArgsT&&...args) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
if (size() == capacity()) {
|
||||
_expand();
|
||||
}
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
size_t p = _next_free();
|
||||
fennec::construct(&_table[p].data, fennec::forward<ArgsT>(args)...);
|
||||
if (n == npos) {
|
||||
if (empty()) {
|
||||
_root = p;
|
||||
_table[p].next = npos;
|
||||
_table[p].prev = npos;
|
||||
} else {
|
||||
_table[p].prev = n;
|
||||
_table[p].next = npos;
|
||||
_last = n;
|
||||
}
|
||||
return;
|
||||
}
|
||||
_table[p].next = n;
|
||||
_table[p].prev = _prev(n);
|
||||
_table[n].prev = p;
|
||||
++_size;
|
||||
}
|
||||
|
||||
void push_front(const value_t& x) {
|
||||
this->insert(0, x);
|
||||
}
|
||||
|
||||
void push_front(value_t&& x) {
|
||||
this->insert(0, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
void emplace_front(ArgsT...args) {
|
||||
this->emplace(0, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
void push_back(const value_t& x) {
|
||||
this->insert(_size, x);
|
||||
}
|
||||
|
||||
void push_back(value_t&& x) {
|
||||
this->insert(_size, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
void emplace_back(ArgsT...args) {
|
||||
this->emplace(_size, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
void erase(size_t i) {
|
||||
size_t j = _walk(i);
|
||||
if (j == npos) return;
|
||||
if (not _table[j].data) return;
|
||||
|
||||
// Get the prev and next indices
|
||||
size_t p = _prev(j);
|
||||
size_t n = _next(j);
|
||||
|
||||
// clear the node
|
||||
_table[j].data = nullopt;
|
||||
_table[j].prev = npos;
|
||||
_table[j].next = npos;
|
||||
|
||||
// Fix prev and next nodes
|
||||
if (p != npos) _table[p].next = n;
|
||||
else _root = n;
|
||||
if (n != npos) _table[n].prev = p;
|
||||
else _last = p;
|
||||
|
||||
// Mark node as freed
|
||||
_freed.push_back(j);
|
||||
}
|
||||
|
||||
void pop_front() {
|
||||
erase(0);
|
||||
}
|
||||
|
||||
void pop_back() {
|
||||
erase(_size - 1);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Access Front Element
|
||||
/// \returns A reference to the first element in the list
|
||||
constexpr value_t& front() {
|
||||
return *_table[_root].data;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Access Front Element
|
||||
/// \returns A const-qualified reference to the first element in the list
|
||||
constexpr const value_t& front() const {
|
||||
return *_table[_root].data;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Access Back Element
|
||||
/// \returns A reference to the last element in the list
|
||||
constexpr value_t& back() {
|
||||
return *_table[_last].data;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Access Back Element
|
||||
/// \returns A const-qualified reference to the last element in the list
|
||||
constexpr const value_t& back() const {
|
||||
return *_table[_last].data;
|
||||
}
|
||||
|
||||
|
||||
// Insertions & Deletions ==============================================================================================
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param it Location to insert at
|
||||
/// \param x value to copy
|
||||
///
|
||||
/// \details \f$O(1)\f$
|
||||
constexpr size_t insert(const iterator& it, const value_t& x) {
|
||||
return this->_insert(it._n, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param it Location to insert at
|
||||
/// \param x value to move
|
||||
///
|
||||
/// \details \f$O(1)\f$
|
||||
constexpr size_t insert(const iterator& it, value_t&& x) {
|
||||
return this->_insert(it._n, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param i Index to insert at
|
||||
/// \param x value to copy
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
constexpr size_t insert(size_t i, const value_t& x) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
return this->_insert(n, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param i Index to insert at
|
||||
/// \param x value to move
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
constexpr size_t insert(size_t i, value_t&& x) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
return this->_insert(n, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param i Index to insert at
|
||||
/// \param args Arguments to construct with
|
||||
///
|
||||
/// \details \f$O(N)\f$
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace(size_t i, ArgsT&&...args) {
|
||||
assert(i <= size(), "Index out of Bounds");
|
||||
|
||||
size_t n = _walk(min(i, size_t(size() - 1)));
|
||||
return this->_insert(n, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Front Copy
|
||||
/// \param x Value to copy
|
||||
constexpr size_t push_front(const value_t& x) {
|
||||
return this->_insert(_root, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Front Move
|
||||
/// \param x Value to move
|
||||
constexpr size_t push_front(value_t&& x) {
|
||||
return this->_insert(_root, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Front
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace_front(ArgsT&&...args) {
|
||||
return this->_insert(_root, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Back Copy
|
||||
/// \param x Value to copy
|
||||
constexpr size_t push_back(const value_t& x) {
|
||||
return this->_insert(npos, x);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Push Back Move
|
||||
/// \param x Value to move
|
||||
constexpr size_t push_back(value_t&& x) {
|
||||
return this->_insert(npos, fennec::forward<value_t>(x));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Back
|
||||
/// \param args Arguments to construct with
|
||||
/// \tparam ArgsT Argument types
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t emplace_back(ArgsT&&...args) {
|
||||
return this->_insert(npos, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase Element
|
||||
/// \param i Index to erase
|
||||
constexpr void erase(size_t i) {
|
||||
assert(i < size(), "Index out of Bounds!");
|
||||
size_t n = _walk(i);
|
||||
_erase(n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase Element
|
||||
/// \param it Location to Erase
|
||||
constexpr void erase(const iterator& it) {
|
||||
_erase(it._n);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pop Front, erases first element
|
||||
constexpr void pop_front() {
|
||||
_erase(_root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pop Back, erases first element
|
||||
constexpr void pop_back() {
|
||||
_erase(_last);
|
||||
}
|
||||
|
||||
// ITERATOR ============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Iterator Class
|
||||
class iterator {
|
||||
public:
|
||||
~iterator() {
|
||||
@@ -352,6 +353,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Iterator Class for Const Access
|
||||
class const_iterator {
|
||||
public:
|
||||
~const_iterator() {
|
||||
@@ -400,18 +403,26 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
///
|
||||
/// \returns An iterator for the first element in the list
|
||||
constexpr iterator begin() {
|
||||
return iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns An iterator for the end of the list
|
||||
constexpr iterator end() {
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const iterator for the first element in the list
|
||||
constexpr const_iterator begin() const {
|
||||
return const_iterator(this, _root);
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const iterator for the end of the list
|
||||
constexpr const_iterator end() const {
|
||||
return const_iterator(this, npos);
|
||||
}
|
||||
@@ -458,15 +469,15 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
size_t _next(size_t n) const {
|
||||
constexpr size_t _next(size_t n) const {
|
||||
return _table[n].next;
|
||||
}
|
||||
|
||||
size_t _prev(size_t n) const {
|
||||
constexpr size_t _prev(size_t n) const {
|
||||
return _table[n].prev;
|
||||
}
|
||||
|
||||
size_t _walk(size_t i) const {
|
||||
constexpr size_t _walk(size_t i) const {
|
||||
size_t n = _root;
|
||||
if (n == npos) return n;
|
||||
while (i > 0 && n != npos) {
|
||||
@@ -475,15 +486,68 @@ private:
|
||||
return n;
|
||||
}
|
||||
|
||||
size_t _next_free() {
|
||||
constexpr size_t _next_free() {
|
||||
if (not _freed.empty()) {
|
||||
size_t n = _freed.back();
|
||||
_freed.pop_back();
|
||||
return n;
|
||||
}
|
||||
_table[_size];
|
||||
return _size;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t _insert(size_t n, ArgsT&&...args) {
|
||||
if (size() == capacity()) {
|
||||
_expand();
|
||||
}
|
||||
|
||||
size_t i = _next_free();
|
||||
++_size;
|
||||
fennec::construct(&_table[i].data, fennec::forward<ArgsT>(args)...);
|
||||
|
||||
if (_root == npos) {
|
||||
_table[i].prev = npos;
|
||||
_table[i].next = npos;
|
||||
_root = _last = i;
|
||||
return i;
|
||||
}
|
||||
|
||||
if (n == npos) {
|
||||
_table[_last].next = i;
|
||||
_table[i].prev = _last;
|
||||
_table[i].next = npos;
|
||||
_last = i;
|
||||
return i;
|
||||
}
|
||||
|
||||
_table[i].prev = _prev(n);
|
||||
_table[i].next = n;
|
||||
_table[n].prev = i;
|
||||
_root = n == _root ? i : _root;
|
||||
return i;
|
||||
}
|
||||
|
||||
constexpr void _erase(size_t n) {
|
||||
if (n == npos) return;
|
||||
|
||||
fennec::destruct(&_table[n].data);
|
||||
_freed.push_back(n);
|
||||
--_size;
|
||||
|
||||
size_t prev = _prev(n);
|
||||
size_t next = _next(n);
|
||||
|
||||
if (prev != npos) {
|
||||
_table[prev].next = next;
|
||||
}
|
||||
|
||||
if (next != npos) {
|
||||
_table[next].prev = prev;
|
||||
}
|
||||
|
||||
_root = (n == _root) ? next : _root;
|
||||
_last = (n == _last) ? prev : _last;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -25,14 +25,12 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// TODO: Document
|
||||
|
||||
/* Ramblings
|
||||
*
|
||||
* Definitions:
|
||||
* user = Programmer using this data structure
|
||||
*
|
||||
* The STL unordered-map is very contrived. Some of its functionality encourages younger programmers to use
|
||||
* The STL maps are very contrived. Some of its functionality encourages younger programmers to use
|
||||
* the exception model. Ideally, I would like this structure to never throw an error with typical use.
|
||||
*
|
||||
* The array access operator is, in my opinion, poorly implemented. I do not think that this operator should handle
|
||||
@@ -40,13 +38,21 @@ namespace fennec
|
||||
* data structure modifies contents by inherently calling operator[].
|
||||
*
|
||||
* Currently, I am considering implementing this as the following:
|
||||
* Access will be handled only via operator[]. Return value will be an optional which forces user validation.
|
||||
* Access will be handled only via operator[]. Return value will be a pointer which forces user validation.
|
||||
* Insertions will be handled only via an insert/emplace function.
|
||||
* Deletions will be handled only via an erase function.
|
||||
*/
|
||||
|
||||
///
|
||||
/// \brief Map for Pairing Values to Keys
|
||||
/// \tparam KeyT The Key Type
|
||||
/// \tparam ValueT The Value Type
|
||||
/// \tparam Hash The Hash to Use
|
||||
/// \tparam Alloc The Allocator to Use
|
||||
template<typename KeyT, typename ValueT, typename Hash = hash<KeyT>, typename Alloc = allocator<pair<KeyT, ValueT>>>
|
||||
struct map {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
using key_t = KeyT;
|
||||
using value_t = ValueT;
|
||||
@@ -68,9 +74,24 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes empty map
|
||||
constexpr map() = default;
|
||||
|
||||
///
|
||||
/// \brief Destructor, Destructs all elements and releases the allocation
|
||||
constexpr ~map() = default;
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Key Access Operator
|
||||
/// \param key Key value to access
|
||||
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
||||
constexpr value_t* operator[](const KeyT& key) {
|
||||
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
||||
pair<KeyT, char[sizeof(ValueT)]> root;
|
||||
@@ -87,6 +108,10 @@ public:
|
||||
return &_set.at(it)->second;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Key Const Access Operator
|
||||
/// \param key Key value to access
|
||||
/// \returns A const-qualified pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
||||
constexpr const value_t* operator[](const KeyT& key) const {
|
||||
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
||||
pair<KeyT, char[sizeof(ValueT)]> root;
|
||||
@@ -103,8 +128,13 @@ public:
|
||||
return &_set.at(it)->second;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr value_t* operator[](ArgsT&&...args) {
|
||||
///
|
||||
/// \brief Argument Key Access Operator
|
||||
/// \tparam ArgT Argument Type
|
||||
/// \param arg Argument to construct the key with
|
||||
/// \returns A pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
||||
template<typename ArgT>
|
||||
constexpr value_t* operator[](ArgT&& arg) {
|
||||
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
||||
pair<KeyT, char[sizeof(ValueT)]> root;
|
||||
pair<KeyT, ValueT> val;
|
||||
@@ -112,7 +142,7 @@ public:
|
||||
~U() {
|
||||
fennec::destruct(&root);
|
||||
}
|
||||
} trick = { .root = { key_t(fennec::forward<ArgsT>(args)...), 0 } }; // Only initialize root
|
||||
} trick = { .root = { key_t(fennec::forward<ArgT>(arg)), 0 } }; // Only initialize root
|
||||
auto it = _set.find(trick.val);
|
||||
if (it == _set.end()) {
|
||||
return nullptr;
|
||||
@@ -120,8 +150,13 @@ public:
|
||||
return &_set.at(it)->second;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr const value_t* operator[](ArgsT&&...args) const {
|
||||
///
|
||||
/// \brief Argument Key Const Access Operator
|
||||
/// \tparam ArgT Argument Type
|
||||
/// \param arg Argument to construct the key with
|
||||
/// \returns A const-qualified pointer to the value associated with `key`, `nullptr` if `key` is not present.
|
||||
template<typename ArgT>
|
||||
constexpr const value_t* operator[](ArgT&& arg) const {
|
||||
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
||||
pair<KeyT, char[sizeof(ValueT)]> root;
|
||||
pair<KeyT, ValueT> val;
|
||||
@@ -129,7 +164,7 @@ public:
|
||||
~U() {
|
||||
fennec::destruct(&root);
|
||||
}
|
||||
} trick = { .root = { key_t(fennec::forward<ArgsT>(args)...), 0 } }; // Only initialize root
|
||||
} trick = { .root = { key_t(fennec::forward<ArgT>(arg)), 0 } }; // Only initialize root
|
||||
auto it = _set.find(trick.val);
|
||||
if (it == _set.end()) {
|
||||
return nullptr;
|
||||
@@ -137,6 +172,12 @@ public:
|
||||
return &_set.at(it)->second;
|
||||
}
|
||||
|
||||
|
||||
// Insertion & Deletion ================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Key-Value Insertion
|
||||
/// \param pair a pair containing the key and its value
|
||||
constexpr void insert(elem_t&& pair) {
|
||||
auto it = _set.find(pair);
|
||||
if (it == _set.end()) {
|
||||
@@ -146,11 +187,17 @@ public:
|
||||
_set.insert(fennec::forward<elem_t>(pair));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Key-Value Insertion
|
||||
/// \param args Arguments for constructing the key-value pair
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
_set.insert(elem_t(args...));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase a key
|
||||
/// \param key key to erase
|
||||
constexpr void erase(KeyT&& key) {
|
||||
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
||||
pair<KeyT, char[sizeof(ValueT)]> root;
|
||||
@@ -163,11 +210,18 @@ public:
|
||||
_set.erase(trick.val);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase a key
|
||||
/// \param key key to erase
|
||||
constexpr void erase(const KeyT& key) {
|
||||
KeyT val = key;
|
||||
erase(fennec::move(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Argument Erase
|
||||
/// \tparam ArgsT Argument Types
|
||||
/// \param args Arguments to construct a key to erase
|
||||
template<typename...ArgsT>
|
||||
constexpr void erase(ArgsT&&...args) {
|
||||
union U { // Hacky way of avoiding constructing the value, TODO: Check for warnings on other compilers
|
||||
|
||||
@@ -37,13 +37,17 @@ constexpr nullopt_t nullopt_v = {};
|
||||
/// \tparam T
|
||||
template<typename T>
|
||||
struct optional {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
// Constructors ========================================================================================================
|
||||
using reference_t = T&;
|
||||
using pointer_t = T*;
|
||||
using const_reference_t = T&;
|
||||
using const_pointer_t = const T*;
|
||||
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Default Constructor
|
||||
constexpr optional()
|
||||
@@ -59,18 +63,18 @@ public:
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Fundamental Type Constructor
|
||||
/// \brief Type Copy Constructor
|
||||
/// \param val the value to initialize the underlying object with
|
||||
constexpr optional(T val) requires(is_fundamental_v<T> or is_pointer_v<T>)
|
||||
constexpr optional(const T& val)
|
||||
: _val(val)
|
||||
, _set(true) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Type Copy Constructor
|
||||
/// \brief Type Move Constructor
|
||||
/// \param val the value to initialize the underlying object with
|
||||
constexpr optional(const T& val)
|
||||
: _val(val)
|
||||
constexpr optional(T&& val)
|
||||
: _val(fennec::forward<T>(val))
|
||||
, _set(true) {
|
||||
}
|
||||
|
||||
@@ -97,6 +101,12 @@ public:
|
||||
opt = nullopt;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr optional(ArgsT&&...args)
|
||||
: _val(fennec::forward<ArgsT>(args)...)
|
||||
, _set(true) {
|
||||
}
|
||||
|
||||
constexpr ~optional() {
|
||||
if constexpr(is_fundamental_v<T>) {
|
||||
return;
|
||||
@@ -111,7 +121,7 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Fundamental Type Assignment
|
||||
/// \val The value to set with
|
||||
/// \param val The value to set with
|
||||
constexpr optional& operator=(nullopt_t) {
|
||||
if constexpr(not is_fundamental_v<T>) {
|
||||
if (_set) {
|
||||
@@ -125,7 +135,7 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Type Copy Assignment
|
||||
/// \val The value to set with
|
||||
/// \param val The value to set with
|
||||
constexpr optional& operator=(const T& val) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
||||
if (_set) {
|
||||
_val = val;
|
||||
@@ -138,12 +148,12 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Type Move Assignment
|
||||
/// \val The value to set with
|
||||
/// \param val The value to set with
|
||||
constexpr optional& operator=(T&& val) requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
||||
if (_set) {
|
||||
_val = fennec::move(val);
|
||||
_val = fennec::forward<T>(val);
|
||||
} else {
|
||||
fennec::construct(&_val, fennec::move(val));
|
||||
fennec::construct(&_val, fennec::forward<T>(val));
|
||||
_set = true;
|
||||
}
|
||||
return *this;
|
||||
@@ -151,7 +161,7 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment
|
||||
/// \val The optional to copy
|
||||
/// \param opt The optional to copy
|
||||
constexpr optional& operator=(const optional& opt) requires is_copy_constructible_v<T> and is_copy_assignable_v<T> {
|
||||
if (_set != opt._set) {
|
||||
_set = opt._set;
|
||||
@@ -169,7 +179,7 @@ public:
|
||||
|
||||
///
|
||||
/// \brief Move Assignment
|
||||
/// \val The optional to move
|
||||
/// \param opt The optional to move
|
||||
constexpr optional& operator=(optional&& opt) noexcept requires is_move_constructible_v<T> and is_move_assignable_v<T> {
|
||||
if (_set != opt._set) {
|
||||
_set = opt._set;
|
||||
@@ -185,6 +195,9 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Assignment
|
||||
/// \val The optional to move
|
||||
template<typename...ArgsT>
|
||||
constexpr T& emplace(ArgsT&&...args) {
|
||||
if (_set) {
|
||||
@@ -196,6 +209,8 @@ public:
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Reset the Optional
|
||||
void reset() {
|
||||
this->operator=(nullopt);
|
||||
}
|
||||
@@ -203,33 +218,51 @@ public:
|
||||
|
||||
// Operators ===========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Implicit Boolean Check, returns `true` when there is a value contained
|
||||
constexpr operator bool() const {
|
||||
return _set;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A pointer to the value, `nullptr` if there is no value
|
||||
constexpr pointer_t operator->() noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns A const-qualified pointer to the value, `nullptr` if there is no value
|
||||
constexpr const_pointer_t operator->() const noexcept {
|
||||
return _set ? &_val : nullptr;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A reference to the value
|
||||
constexpr T& operator*() & noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Dereference Operator
|
||||
/// \returns A const-qualified reference to the value
|
||||
constexpr const T& operator*() const& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Dereference Operator
|
||||
/// \returns A reference to the value
|
||||
constexpr T&& operator*() && noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Const Dereference Operator
|
||||
/// \returns A const-qualified reference to the value
|
||||
constexpr const T&& operator*() const&& noexcept {
|
||||
assertd(_set, "Attempted to reference the value of an unset optional");
|
||||
return _val;
|
||||
|
||||
@@ -28,57 +28,124 @@ namespace fennec
|
||||
|
||||
// TODO: Document
|
||||
|
||||
///
|
||||
/// \brief Struct for holding a pair of values
|
||||
/// \tparam T0 The type of the first value
|
||||
/// \tparam T1 The type of the second value
|
||||
template<typename T0, typename T1>
|
||||
struct pair {
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, invokes default constructor for both elements
|
||||
constexpr pair() = default;
|
||||
|
||||
///
|
||||
/// \brief Destructor, invokes destructor for both elements
|
||||
constexpr ~pair() = default;
|
||||
|
||||
///
|
||||
/// \brief Pair Copy Constructor
|
||||
/// \param x Value to copy for the first element
|
||||
/// \param y Value to copy for the first element
|
||||
constexpr pair(const T0& x, const T1& y)
|
||||
: first(x)
|
||||
, second(y) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pair Move Constructor
|
||||
/// \param x Value to move for the first element
|
||||
/// \param y Value to move for the first element
|
||||
constexpr pair(T0&& x, T1&& y) noexcept
|
||||
: first(fennec::forward<T0>(x))
|
||||
, second(fennec::forward<T1>(y)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Pair Implicit Constructor
|
||||
/// \param arg1 Value to initialize the first element
|
||||
/// \param arg2 Value to initialize the first element
|
||||
template<typename Arg1T, typename Arg2T>
|
||||
constexpr pair(Arg1T&& arg1, Arg2T&& arg2)
|
||||
: first(fennec::forward<Arg1T>(arg1))
|
||||
, second(fennec::forward<Arg2T>(arg2)) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Constructor, copies both elements
|
||||
constexpr pair(const pair&) = default;
|
||||
|
||||
///
|
||||
/// \brief Move Constructor, moves both elements
|
||||
constexpr pair(pair&&) noexcept = default;
|
||||
|
||||
///
|
||||
/// \brief Copy Assignment, copies both elements
|
||||
constexpr pair& operator=(const pair&) = default;
|
||||
|
||||
///
|
||||
/// \brief Move Assignment, moves both elements
|
||||
constexpr pair& operator=(pair&&) noexcept = default;
|
||||
|
||||
|
||||
// Comparison ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Equality Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns `true` when both elements of each pair are equal
|
||||
constexpr bool operator==(const pair& p) const {
|
||||
return first == p.first and second == p.second;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Inequality Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns `true` when either element of each pair are equal
|
||||
constexpr bool operator!=(const pair& p) const {
|
||||
return first != p.first or second != p.second;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Less Than Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is less, or they are
|
||||
/// equal and the second element is less
|
||||
constexpr bool operator<(const pair& p) const {
|
||||
return first < p.first or (first == p.first and second < p.second);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Less Equal Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is less, or they are
|
||||
/// equal and the second element is less or equal
|
||||
constexpr bool operator<=(const pair& p) const {
|
||||
return first < p.first or (first == p.first and second <= p.second);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Greater Than Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is greater, or they are
|
||||
/// equal and the second element is greater
|
||||
constexpr bool operator>(const pair& p) const {
|
||||
return first > p.first or (first == p.first and second > p.second);
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Greater Equal Operator
|
||||
/// \param p Pair to compare with
|
||||
/// \returns lexical comparison of both elements, i.e. returns `true` when the first element is greater, or they are
|
||||
/// equal and the second element is greater or equal
|
||||
constexpr bool operator>=(const pair& p) const {
|
||||
return first > p.first or (first == p.first and second >= p.second);
|
||||
}
|
||||
|
||||
// Members =============================================================================================================
|
||||
|
||||
T0 first;
|
||||
T1 second;
|
||||
};
|
||||
|
||||
@@ -26,9 +26,15 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
///
|
||||
/// \brief Rooted-Directed Tree
|
||||
/// \tparam TypeT Data type
|
||||
/// \tparam AllocT Allocator Type
|
||||
template<typename TypeT, typename AllocT = allocator<TypeT>>
|
||||
struct rdtree {
|
||||
private:
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
protected:
|
||||
struct node;
|
||||
|
||||
public:
|
||||
@@ -63,7 +69,7 @@ public:
|
||||
template<typename...ArgsT>
|
||||
explicit constexpr rdtree(ArgsT&&...args)
|
||||
: _data(), _freed(), _size(1) {
|
||||
_data.callocate(10);
|
||||
_data.callocate(8);
|
||||
fennec::construct(&_data[0], npos, npos, npos, npos, fennec::forward<ArgsT>(args)...);
|
||||
}
|
||||
|
||||
@@ -78,51 +84,84 @@ public:
|
||||
|
||||
// Assignment ==========================================================================================================
|
||||
|
||||
friend constexpr rdtree& operator=(rdtree& lhs, const rdtree& rhs) {
|
||||
for (value_t* it : lhs._data) {
|
||||
constexpr rdtree& operator=(const rdtree& rhs) {
|
||||
for (value_t* it : this->_data) {
|
||||
fennec::destruct(it);
|
||||
}
|
||||
lhs._data = rhs._data;
|
||||
lhs._freed = rhs._freed;
|
||||
lhs._size = rhs._size;
|
||||
return lhs;
|
||||
_data = rhs._data;
|
||||
_freed = rhs._freed;
|
||||
_size = rhs._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
friend constexpr rdtree& operator=(rdtree& lhs, rdtree&& rhs) noexcept {
|
||||
for (value_t* it : lhs._data) {
|
||||
constexpr rdtree& operator=(rdtree&& rhs) noexcept {
|
||||
for (value_t* it : _data) {
|
||||
fennec::destruct(it);
|
||||
}
|
||||
lhs._data = fennec::move(rhs._data);
|
||||
lhs._freed = fennec::move(rhs._freed);
|
||||
lhs._size = rhs._size;
|
||||
return lhs;
|
||||
_data = fennec::move(rhs._data);
|
||||
_freed = fennec::move(rhs._freed);
|
||||
_size = rhs._size;
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
constexpr size_t capacity() const {
|
||||
return _data.capacity();
|
||||
}
|
||||
|
||||
constexpr bool empty() const {
|
||||
return _size == 0;
|
||||
}
|
||||
|
||||
|
||||
// Access ==============================================================================================================
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The id of the parent node
|
||||
constexpr size_t parent(size_t i) const {
|
||||
return _data[i].parent;
|
||||
return i == npos ? npos : _data[i].parent;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The id of the child node
|
||||
constexpr size_t child(size_t i) const {
|
||||
return _data[i].child;
|
||||
return i == npos ? npos : _data[i].child;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The id of the next node
|
||||
constexpr size_t next(size_t i) const {
|
||||
return _data[i].next;
|
||||
return i == npos ? npos : _data[i].next;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to check
|
||||
/// \returns The id of the previous node
|
||||
constexpr size_t prev(size_t i) const {
|
||||
return _data[i].prev;
|
||||
return i == npos ? npos : _data[i].prev;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to access
|
||||
/// \returns A reference to the value of the node wrapped in an optional
|
||||
constexpr optional<value_t>& operator[](size_t i) {
|
||||
return *_data[i].value;
|
||||
return _data[i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \param i The id of the node to access
|
||||
/// \returns A const-qualified reference to the value of the node wrapped in an optional
|
||||
constexpr const optional<value_t>& operator[](size_t i) const {
|
||||
return *_data[i].value;
|
||||
return _data[i].value;
|
||||
}
|
||||
|
||||
|
||||
@@ -134,21 +173,7 @@ public:
|
||||
/// \param val the value to insert
|
||||
/// \returns the index of the created node
|
||||
constexpr size_t insert(size_t parent, const value_t& val) {
|
||||
if (parent == npos || _size == 0) {
|
||||
if (_size == 0) {
|
||||
fennec::construct(&_data[root], npos, npos, npos, npos, val);
|
||||
_size = 1;
|
||||
} else {
|
||||
_data[root].value = val;
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
size_t i = _next_free();
|
||||
size_t n = child(parent);
|
||||
_data[parent].child = i;
|
||||
fennec::construct(&_data[i], parent, npos, n, npos);
|
||||
return i;
|
||||
return this->_insert(parent, val);
|
||||
}
|
||||
|
||||
///
|
||||
@@ -157,42 +182,14 @@ public:
|
||||
/// \param val the value to insert
|
||||
/// \returns the index of the created node
|
||||
constexpr size_t insert(size_t parent, value_t&& val) {
|
||||
if (parent == npos || _size == 0) {
|
||||
if (_size == 0) {
|
||||
fennec::construct(&_data[root], npos, npos, npos, npos, fennec::forward<value_t>(val));
|
||||
_size = 1;
|
||||
} else {
|
||||
_data[root].value = fennec::forward<value_t>(val);
|
||||
}
|
||||
return root;
|
||||
}
|
||||
|
||||
size_t i = _next_free();
|
||||
size_t n = child(parent);
|
||||
_data[parent].child = i;
|
||||
fennec::construct(&_data[i], parent, npos, n, npos);
|
||||
return i;
|
||||
return this->_insert(parent, fennec::forward<value_t>(val));
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Erase a node in the tree and all of it's children
|
||||
/// \param i the index of the node
|
||||
constexpr void erase(size_t i) {
|
||||
list<size_t> queue;
|
||||
if (child(i) != npos) {
|
||||
queue.push_back(i);
|
||||
}
|
||||
|
||||
while (not queue.empty()) {
|
||||
size_t n = queue.front(); queue.pop_front();
|
||||
if (next(n) != npos) queue.push_back(next(n));
|
||||
if (child(n) != npos) queue.push_back(child(n));
|
||||
fennec::destruct(&_data[n]);
|
||||
_freed.push_back(n);
|
||||
}
|
||||
|
||||
fennec::destruct(&_data[i]);
|
||||
_freed.push_back(i);
|
||||
_erase(i);
|
||||
}
|
||||
|
||||
|
||||
@@ -206,13 +203,57 @@ protected:
|
||||
}
|
||||
|
||||
size_t _next_free() {
|
||||
size_t next = _size + 1;
|
||||
size_t next = _size++;
|
||||
if (not _freed.empty()) {
|
||||
next = _freed.back();
|
||||
_freed.pop_back();
|
||||
}
|
||||
if (_size >= capacity()) {
|
||||
_expand();
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr size_t _insert(size_t p, ArgsT&&...args) {
|
||||
if (_size == 0) {
|
||||
fennec::construct(&_data[root], npos, npos, npos, npos, fennec::forward<ArgsT>(args)...);
|
||||
_size = 1;
|
||||
return root;
|
||||
}
|
||||
|
||||
if (p == npos) {
|
||||
_data[root].value = value_t(fennec::forward<ArgsT>(args)...);
|
||||
return root;
|
||||
}
|
||||
|
||||
size_t i = _next_free();
|
||||
size_t n = child(p);
|
||||
_data[p].child = i;
|
||||
if (n != npos) _data[n].prev = i;
|
||||
fennec::construct(&_data[i], p, npos, npos, n, fennec::forward<ArgsT>(args)...);
|
||||
return i;
|
||||
}
|
||||
|
||||
constexpr void _erase(size_t i) {
|
||||
list<size_t> queue;
|
||||
size_t j = 0;
|
||||
queue.push_back(child(i));
|
||||
while (queue.empty() == false) {
|
||||
size_t n = queue.front(); queue.pop_front();
|
||||
if (n == npos) continue;
|
||||
queue.push_back(next(n));
|
||||
queue.push_back(child(n));
|
||||
fennec::destruct(&_data[n]);
|
||||
_freed.push_back(n);
|
||||
--_size;
|
||||
++j;
|
||||
}
|
||||
|
||||
fennec::destruct(&_data[i]);
|
||||
_freed.push_back(i);
|
||||
--_size;
|
||||
}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -31,15 +31,30 @@
|
||||
namespace fennec
|
||||
{
|
||||
|
||||
// TODO: Document
|
||||
|
||||
template<typename T, class Hash = hash<T>, class Equals = equality<T>, class Alloc = allocator<T>>
|
||||
///
|
||||
///
|
||||
/// \brief wrapper for sets of elements
|
||||
/// \details
|
||||
/// This data-structure behaves like a set, but does not use pointers, instead storing the table in-array
|
||||
///
|
||||
/// | Property | Value |
|
||||
/// |:----------|:----------:|
|
||||
/// | stable | \emoji x |
|
||||
/// | access | \f$O(1)\f$ |
|
||||
/// | insertion | \f$O(1)\f$ |
|
||||
/// | deletion | \f$O(1)\f$ |
|
||||
/// | space | \f$O(1)\f$ |
|
||||
///
|
||||
/// \tparam TypeT The type to contain
|
||||
template<typename TypeT, class Hash = hash<TypeT>, class Equals = equality<TypeT>, class Alloc = allocator<TypeT>>
|
||||
struct set {
|
||||
|
||||
// Definitions =========================================================================================================
|
||||
public:
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<T>;
|
||||
using alloc_t = typename allocator_traits<Alloc>::template rebind<TypeT>;
|
||||
using hash_t = Hash;
|
||||
using equal_t = Equals;
|
||||
using elem_t = T;
|
||||
using elem_t = TypeT;
|
||||
|
||||
class iterator;
|
||||
static constexpr size_t npos = -1;
|
||||
@@ -54,7 +69,11 @@ private:
|
||||
constexpr ~node() = default;
|
||||
};
|
||||
|
||||
// Constructors ========================================================================================================
|
||||
public:
|
||||
|
||||
///
|
||||
/// \brief Default Constructor, initializes empty set
|
||||
constexpr set()
|
||||
: _alloc()
|
||||
, _hash()
|
||||
@@ -63,6 +82,8 @@ public:
|
||||
, _load(default_load) {
|
||||
};
|
||||
|
||||
///
|
||||
/// \brief Hash Copy Constructor, initializes empty set with a hash
|
||||
constexpr set(const hash_t& hash)
|
||||
: _alloc()
|
||||
, _hash(hash)
|
||||
@@ -71,6 +92,8 @@ public:
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Move Constructor, initializes empty set with a hash
|
||||
constexpr set(hash_t&& hash) noexcept
|
||||
: _alloc()
|
||||
, _hash(hash)
|
||||
@@ -79,6 +102,8 @@ public:
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Copy Constructor, initializes empty set with an allocator
|
||||
constexpr set(const alloc_t& alloc)
|
||||
: _alloc(alloc)
|
||||
, _hash()
|
||||
@@ -87,6 +112,8 @@ public:
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Alloc Move Constructor, initializes empty set with an allocator
|
||||
constexpr set(alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash()
|
||||
@@ -95,6 +122,8 @@ public:
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Alloc Copy Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(const hash_t& hash, const alloc_t& alloc)
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
@@ -103,6 +132,8 @@ public:
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Copy Alloc Move Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(const hash_t& hash, alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
@@ -111,6 +142,8 @@ public:
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Move Alloc Move Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(hash_t&& hash, alloc_t&& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
@@ -119,6 +152,8 @@ public:
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Hash Move Alloc Copy Constructor, initializes empty set with a hash and allocator
|
||||
constexpr set(hash_t&& hash, const alloc_t& alloc) noexcept
|
||||
: _alloc(alloc)
|
||||
, _hash(hash)
|
||||
@@ -127,6 +162,9 @@ public:
|
||||
, _load(default_load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Set Copy Constructor
|
||||
/// \param set Set to copy
|
||||
constexpr set(const set& set)
|
||||
: _alloc(set._alloc)
|
||||
, _hash(set._hash)
|
||||
@@ -135,6 +173,9 @@ public:
|
||||
, _load(set._load) {
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Set Move Constructor
|
||||
/// \param set Set to move
|
||||
constexpr set(set&& set) noexcept
|
||||
: _alloc(fennec::move(set._alloc))
|
||||
, _hash(fennec::move(set._hash))
|
||||
@@ -143,51 +184,36 @@ public:
|
||||
, _load(set._load) {
|
||||
}
|
||||
|
||||
constexpr ~set() = default;
|
||||
///
|
||||
/// \brief Destructor, destructs all elements and releases the allocation
|
||||
constexpr ~set() {
|
||||
for (size_t i = 0; i < capacity(); ++i) {
|
||||
_alloc[i].value = nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Properties ==========================================================================================================
|
||||
|
||||
///
|
||||
/// \returns Size of the set in elements
|
||||
constexpr size_t size() const {
|
||||
return _size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \returns Capacity of the set in elements
|
||||
constexpr size_t capacity() const {
|
||||
return _alloc.capacity();
|
||||
}
|
||||
|
||||
constexpr void insert(elem_t&& val) {
|
||||
if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full
|
||||
_expand();
|
||||
}
|
||||
|
||||
elem_t value = fennec::forward<elem_t>(val);
|
||||
size_t i = _hash(value) % capacity(); // Initial search index
|
||||
int psl = 0;
|
||||
while (_alloc[i].value) { // Search for empty cell
|
||||
if (_equal(*_alloc[i].value, val)) { // Check to see if this element is already inserted
|
||||
return;
|
||||
}
|
||||
if (psl > _alloc[i].psl) { // When psl is higher, swap
|
||||
_sumpsl += psl - _alloc[i].psl;
|
||||
fennec::swap(_alloc[i].psl, psl);
|
||||
fennec::swap(*_alloc[i].value, value);
|
||||
}
|
||||
i = (i + 1) % capacity(); ++psl;
|
||||
}
|
||||
_alloc[i].value = fennec::move(value);
|
||||
_sumpsl += (_alloc[i].psl = psl);
|
||||
++_size;
|
||||
}
|
||||
|
||||
constexpr void insert(const elem_t& val) {
|
||||
elem_t value = val; // Copy Constructor invoked here
|
||||
this->insert(fennec::move(value)); // Only invokes moves
|
||||
}
|
||||
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
elem_t value = elem_t(fennec::forward<ArgsT>(args)...); // Constructor invoked here
|
||||
this->insert(fennec::move(value)); // Only invokes moves
|
||||
}
|
||||
// Access ==============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Find an Element
|
||||
/// \param val Value to find
|
||||
/// \returns An iterator at the location of the value
|
||||
constexpr iterator find(const elem_t& val) const {
|
||||
size_t s = _hash(val) % capacity(); // Initial search index
|
||||
int psl = (_size != 0) ? _sumpsl / _size : 0; // Initial psl
|
||||
@@ -229,24 +255,83 @@ public:
|
||||
return iterator(this, npos);
|
||||
}
|
||||
|
||||
constexpr elem_t* at(const iterator& it) {
|
||||
size_t i = it._i;
|
||||
if (i >= capacity()) return nullptr;
|
||||
if (not _alloc[i].value) return nullptr;
|
||||
return &*_alloc[i].value;
|
||||
}
|
||||
|
||||
constexpr const elem_t* at(const iterator& it) const {
|
||||
size_t i = it._i;
|
||||
if (i >= capacity()) return nullopt;
|
||||
if (not _alloc[i].value) return nullopt;
|
||||
return &*_alloc[i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Check if a set contains a value
|
||||
/// \param val Value to check
|
||||
/// \returns `true` if `val` can be found, `false` otherwise
|
||||
constexpr bool contains(const elem_t& val) const {
|
||||
return this->find(val) != end();
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Iterator Access
|
||||
/// \param it Location to access
|
||||
/// \returns A pointer to the element, `nullptr` if not found.
|
||||
/// The value should not be changed in a manner that will change the hash of the element.
|
||||
constexpr elem_t* at(const iterator& it) {
|
||||
if (not _alloc[it._i].value) return nullptr;
|
||||
return &*_alloc[it._i].value;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Iterator Const Access
|
||||
/// \param it Location to access
|
||||
/// \returns A const-qualified pointer to the element, `nullptr` if not found.
|
||||
constexpr const elem_t* at(const iterator& it) const {
|
||||
if (not _alloc[it._i].value) return nullptr;
|
||||
return &*_alloc[it._i].value;
|
||||
}
|
||||
|
||||
// Insertion & Deletion ================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Move Insertion
|
||||
/// \param val Value to insert
|
||||
constexpr void insert(elem_t&& val) {
|
||||
if (_size == 0 or double(_size) / capacity() >= _load) { // expand when full
|
||||
_expand();
|
||||
}
|
||||
|
||||
elem_t value = fennec::forward<elem_t>(val);
|
||||
size_t i = _hash(value) % capacity(); // Initial search index
|
||||
int psl = 0;
|
||||
while (_alloc[i].value) { // Search for empty cell
|
||||
if (_equal(*_alloc[i].value, val)) { // Check to see if this element is already inserted
|
||||
return;
|
||||
}
|
||||
if (psl > _alloc[i].psl) { // When psl is higher, swap
|
||||
_sumpsl += psl - _alloc[i].psl;
|
||||
fennec::swap(_alloc[i].psl, psl);
|
||||
fennec::swap(*_alloc[i].value, value);
|
||||
}
|
||||
i = (i + 1) % capacity(); ++psl;
|
||||
}
|
||||
_alloc[i].value = fennec::move(value);
|
||||
_sumpsl += (_alloc[i].psl = psl);
|
||||
++_size;
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Copy Insertion
|
||||
/// \param val Value to insert
|
||||
constexpr void insert(const elem_t& val) {
|
||||
elem_t value = val; // Copy Constructor invoked here
|
||||
this->insert(fennec::move(value)); // Only invokes moves
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Emplace Insertion
|
||||
/// \tparam ArgsT Argument types
|
||||
/// \param args Arguments to construct with
|
||||
template<typename...ArgsT>
|
||||
constexpr void emplace(ArgsT&&...args) {
|
||||
elem_t value = elem_t(fennec::forward<ArgsT>(args)...); // Constructor invoked here
|
||||
this->insert(fennec::move(value)); // Only invokes moves
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Element Erase
|
||||
/// \param it Location to erase
|
||||
constexpr void erase(iterator it) {
|
||||
size_t i = it._i;
|
||||
if (i >= capacity()) {
|
||||
@@ -269,6 +354,9 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
///
|
||||
/// \brief Element Erase
|
||||
/// \param val Value to erase
|
||||
constexpr void erase(const elem_t& val) {
|
||||
this->erase(this->find(val));
|
||||
}
|
||||
@@ -276,6 +364,8 @@ public:
|
||||
|
||||
// ITERATOR ============================================================================================================
|
||||
|
||||
///
|
||||
/// \brief Class for Iterating the Set
|
||||
class iterator {
|
||||
public:
|
||||
constexpr ~iterator() {
|
||||
@@ -303,6 +393,11 @@ public:
|
||||
return *_set->_alloc[_i].value;
|
||||
}
|
||||
|
||||
constexpr const elem_t* operator->() const {
|
||||
if (not _set->_alloc[_i].value) return nullptr;
|
||||
return &*_set->_alloc[_i].value;
|
||||
}
|
||||
|
||||
constexpr bool operator==(const iterator& it) {
|
||||
return _set == it._set and _i == it._i;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user