Compare commits
4 commits
master
...
scapegoatt
Author | SHA1 | Date | |
---|---|---|---|
|
e3f902d6e3 | ||
|
3f633df74f | ||
|
e05386eafe | ||
|
5c4cacf286 |
7 changed files with 933 additions and 0 deletions
48
include/duckhandy/alignment.hpp
Normal file
48
include/duckhandy/alignment.hpp
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
/* Copyright 2016-2025 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* "duckhandy" is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef id99DC0F782D0F4907A7768E8743A8BE74
|
||||||
|
#define id99DC0F782D0F4907A7768E8743A8BE74
|
||||||
|
|
||||||
|
#include <ciso646>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstddef>
|
||||||
|
|
||||||
|
namespace dhandy {
|
||||||
|
template <unsigned int V>
|
||||||
|
concept is_pow_of_two = static_cast<bool>(V and not(V bitand (V - 1u)));
|
||||||
|
|
||||||
|
template <unsigned int V, typename T>
|
||||||
|
requires is_pow_of_two<V>
|
||||||
|
constexpr T align_to (T v) {
|
||||||
|
return (v + V-1) & ~(T{V-1});
|
||||||
|
}
|
||||||
|
|
||||||
|
template <unsigned int V, typename T>
|
||||||
|
requires is_pow_of_two<V>
|
||||||
|
constexpr T padding_to (T v) {
|
||||||
|
return (-v) & (V-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <unsigned int V, typename T>
|
||||||
|
requires is_pow_of_two<V>
|
||||||
|
constexpr T* align_ptr_to (T* ptr, std::size_t add) {
|
||||||
|
return reinterpret_cast<T*>(align_to<V>(reinterpret_cast<std::uintptr_t>(ptr) + add));
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace dhandy
|
||||||
|
#endif
|
252
include/duckhandy/implem/tree_iterator.inl
Normal file
252
include/duckhandy/implem/tree_iterator.inl
Normal file
|
@ -0,0 +1,252 @@
|
||||||
|
/* Copyright 2016-2024 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* "duckhandy" is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace dhandy {
|
||||||
|
namespace implem {
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename P>
|
||||||
|
bool TreeIterator_base<P>::Exhausted() const {
|
||||||
|
return m_stack.empty();
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename P>
|
||||||
|
TreeIterator_base<P>::TreeIterator_base (const TreeIterator_base& parOther) :
|
||||||
|
m_stack(parOther.m_stack)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename P>
|
||||||
|
template <typename P1>
|
||||||
|
TreeIterator_base<P>::TreeIterator_base (const TreeIterator_base<P1>& parOther) {
|
||||||
|
typename TreeIterator_base<P1>::StackType otherStackCopy(parOther.m_stack);
|
||||||
|
std::vector<P> localCopy;
|
||||||
|
localCopy.reserve(parOther.m_stack.size());
|
||||||
|
while (not otherStackCopy.empty()) {
|
||||||
|
P convertedItem = otherStackCopy.top();
|
||||||
|
localCopy.push_back(convertedItem);
|
||||||
|
otherStackCopy.pop();
|
||||||
|
}
|
||||||
|
m_stack.reserve(parOther.m_stack.capacity());
|
||||||
|
for (typename std::vector<P>::reverse_iterator itRev = localCopy.rbegin(), itRevEnd = localCopy.rend(); itRev != itRevEnd; ++itRev) {
|
||||||
|
assert(m_stack.capacity() > m_stack.size());
|
||||||
|
m_stack.push(*itRev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, false>::reference TreeIterator_const_layer<T, N, false>::operator* () {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
return this->m_stack.top()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, false>::const_reference TreeIterator_const_layer<T, N, false>::operator* () const {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
return this->m_stack.top()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, false>::pointer TreeIterator_const_layer<T, N, false>::operator-> () {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
return &this->m_stack.top()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, false>::const_pointer TreeIterator_const_layer<T, N, false>::operator-> () const {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
return &this->m_stack.top()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
N* TreeIterator_const_layer<T, N, false>::GetPointer() {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
return this->m_stack.top();
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
const N* TreeIterator_const_layer<T, N, false>::GetPointer() const {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
return this->m_stack.top();
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, true>::const_reference TreeIterator_const_layer<T, N, true>::operator* () const {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
return this->m_stack.top()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
typename TreeIterator_const_layer<T, N, true>::const_pointer TreeIterator_const_layer<T, N, true>::operator-> () const {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
return &this->m_stack.top()->content;
|
||||||
|
}
|
||||||
|
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
///---------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
const N* TreeIterator_const_layer<T, N, true>::GetPointer() const {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
return this->m_stack.top();
|
||||||
|
}
|
||||||
|
} //namespace implem
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>::TreeIterator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>::TreeIterator (const TreeIterator& parOther) :
|
||||||
|
parent_type(parOther)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
template <typename T1>
|
||||||
|
TreeIterator<T, N>::TreeIterator (const TreeIterator<T1, N>& parOther) :
|
||||||
|
parent_type(parOther)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
template <typename S>
|
||||||
|
TreeIterator<T, N>::TreeIterator (S parCopyStackBottomUp, size_type parStackLen, size_type parMaxDepthHint) {
|
||||||
|
assert(parStackLen > 0);
|
||||||
|
this->m_stack.reserve(std::max(parStackLen, parMaxDepthHint));
|
||||||
|
typename StackType::value_type prevNode = *parCopyStackBottomUp;
|
||||||
|
++parCopyStackBottomUp;
|
||||||
|
this->m_stack.push(prevNode);
|
||||||
|
|
||||||
|
for (size_type z = 1; z < parStackLen; ++z) {
|
||||||
|
typename StackType::value_type currNode = *parCopyStackBottomUp;
|
||||||
|
if (prevNode->left == currNode) {
|
||||||
|
assert(this->m_stack.capacity() > this->m_stack.size());
|
||||||
|
this->m_stack.push(currNode);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//If you get this assertion make sure the iterator you are
|
||||||
|
//passing in is reversed (ie: from leaf to root)
|
||||||
|
assert(currNode == prevNode->right);
|
||||||
|
}
|
||||||
|
|
||||||
|
prevNode = currNode;
|
||||||
|
++parCopyStackBottomUp;
|
||||||
|
}
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>::TreeIterator (NodeTypePointer parRoot, size_type parMaxDepthHint) {
|
||||||
|
if (parMaxDepthHint > 0)
|
||||||
|
this->m_stack.reserve(parMaxDepthHint);
|
||||||
|
RecurseLeft(parRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>::~TreeIterator() {
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///Post-increment
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N> TreeIterator<T, N>::operator++ (int) {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
TreeIterator<T, N> retVal = *this;
|
||||||
|
++(*this);
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///Pre-increment
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
TreeIterator<T, N>& TreeIterator<T, N>::operator++() {
|
||||||
|
assert(not this->Exhausted());
|
||||||
|
NodeTypePointer currNode = this->m_stack.top();
|
||||||
|
#if defined(ASSERTIONSENABLED)
|
||||||
|
const size_type stackCapacity = this->m_stack.capacity();
|
||||||
|
#endif
|
||||||
|
this->m_stack.pop();
|
||||||
|
#if defined(ASSERTIONSENABLED)
|
||||||
|
//It shouldn't normally happen, but it's just to make sure
|
||||||
|
assert(stackCapacity == this->m_stack.capacity());
|
||||||
|
#endif
|
||||||
|
RecurseLeft(currNode->right);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
const TreeIterator<T, N>& TreeIterator<T, N>::operator= (const TreeIterator& parOther) {
|
||||||
|
this->m_stack = parOther.m_stack;
|
||||||
|
assert(this->m_stack.capacity() >= parOther.m_stack.capacity());
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
bool TreeIterator<T, N>::operator== (const TreeIterator& parOther) const {
|
||||||
|
return this->m_stack.size() == parOther.m_stack.size() and (this->m_stack.empty() or parOther.m_stack.top() == this->m_stack.top());
|
||||||
|
}
|
||||||
|
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
///-------------------------------------------------------------------------
|
||||||
|
template <typename T, typename N>
|
||||||
|
void TreeIterator<T, N>::RecurseLeft (NodeTypePointer parFrom) {
|
||||||
|
NodeTypePointer currNode = parFrom;
|
||||||
|
while (NULL != currNode) {
|
||||||
|
assert(this->m_stack.capacity() > this->m_stack.size());
|
||||||
|
this->m_stack.push(currNode);
|
||||||
|
currNode = currNode->left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} //namespace dhandy
|
268
include/duckhandy/small_object_allocator.hpp
Normal file
268
include/duckhandy/small_object_allocator.hpp
Normal file
|
@ -0,0 +1,268 @@
|
||||||
|
/* Copyright 2016-2024 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* "duckhandy" is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef idE77208CAFC79452DA12757DD0F6692D3
|
||||||
|
#define idE77208CAFC79452DA12757DD0F6692D3
|
||||||
|
|
||||||
|
#include "alignment.hpp"
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cassert>
|
||||||
|
#include <climits>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <ciso646>
|
||||||
|
#include <memory>
|
||||||
|
#include <strings.h>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstring>
|
||||||
|
#include <iterator>
|
||||||
|
#include <vector>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#if !defined(NDEBUG) && !defined(DEBUG_SMALL_OBJECT_ALLOCATOR)
|
||||||
|
# define DEBUG_SMALL_OBJECT_ALLOCATOR
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(DEBUG_SMALL_OBJECT_ALLOCATOR)
|
||||||
|
# include "tiger_bt.hpp"
|
||||||
|
# include "lengthof.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
namespace dhandy {
|
||||||
|
namespace implem {
|
||||||
|
[[gnu::pure,gnu::always_inline]]
|
||||||
|
unsigned int ffs (unsigned int val) { return static_cast<unsigned int>(::ffs(static_cast<int>(val))); }
|
||||||
|
|
||||||
|
[[gnu::pure,gnu::always_inline]]
|
||||||
|
unsigned int ffs (unsigned long val) { return static_cast<unsigned int>(::ffsl(static_cast<long>(val))); }
|
||||||
|
|
||||||
|
[[gnu::pure,gnu::always_inline]]
|
||||||
|
unsigned int ffs (unsigned long long val) { return static_cast<unsigned int>(::ffsll(static_cast<long long>(val))); }
|
||||||
|
|
||||||
|
#if defined(DEBUG_SMALL_OBJECT_ALLOCATOR)
|
||||||
|
template <typename T, template <class> typename A>
|
||||||
|
consteval std::uint32_t make_signature() {
|
||||||
|
return static_cast<std::uint32_t>(
|
||||||
|
bt::tiger(__PRETTY_FUNCTION__, lengthof(__PRETTY_FUNCTION__), bt::TigerPaddingV2).a & 0xFFFFFFFF
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
} //namespace implem
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct AllocatorFunction {
|
||||||
|
std::unique_ptr<T> operator()() {
|
||||||
|
return std::make_unique<T>();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, template <class> typename A=AllocatorFunction>
|
||||||
|
class SmallObjectAllocator {
|
||||||
|
public:
|
||||||
|
typedef T value_type;
|
||||||
|
typedef T* pointer;
|
||||||
|
typedef const T* const_pointer;
|
||||||
|
typedef T& reference;
|
||||||
|
typedef const T& const_reference;
|
||||||
|
typedef std::size_t size_type;
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
typedef std::true_type propagate_on_container_move_assignment;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef std::uint_fast32_t uint_freelist_t;
|
||||||
|
typedef std::uint32_t index_t;
|
||||||
|
|
||||||
|
struct TAndPtrFakeStruct { //only used for sizeof()
|
||||||
|
TAndPtrFakeStruct() = delete;
|
||||||
|
T t;
|
||||||
|
#if defined(DEBUG_SMALL_OBJECT_ALLOCATOR)
|
||||||
|
std::uint32_t signature;
|
||||||
|
#endif
|
||||||
|
index_t block_index; //index in a lookup table so that lookup[index-1]->next == owner
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr std::uint32_t signature = implem::make_signature<T, A>();
|
||||||
|
static constexpr size_type size = sizeof(TAndPtrFakeStruct);
|
||||||
|
static constexpr size_type align = alignof(TAndPtrFakeStruct);
|
||||||
|
static constexpr size_type object_size = sizeof(T); //(size < sizeof(T*) ? sizeof(T*) : size);
|
||||||
|
static constexpr size_type object_align = alignof(T); //(align < alignof(T*) ? alignof(T*) : align);
|
||||||
|
static constexpr size_type block_ptr_offset = offsetof(TAndPtrFakeStruct, block);
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr size_type objects_per_block = CHAR_BIT * sizeof(uint_freelist_t);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct Block {
|
||||||
|
typedef typename std::aligned_storage<size, align>::type raw_t;
|
||||||
|
|
||||||
|
raw_t data[objects_per_block];
|
||||||
|
std::unique_ptr<Block> next;
|
||||||
|
uint_freelist_t freelist{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
SmallObjectAllocator() = default;
|
||||||
|
~SmallObjectAllocator() noexcept = default;
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr pointer allocate (size_type size);
|
||||||
|
//[[nodiscard]] constexpr std::allocation_result<pointer, size_type> allocate_at_least (size_type size);
|
||||||
|
constexpr void deallocate (pointer ptr, size_type size);
|
||||||
|
|
||||||
|
//template <typename T1, typename T2>
|
||||||
|
//constexpr bool operator== (const allocator<T1& lh, const allocator<T2>& rhs) noexcept;
|
||||||
|
|
||||||
|
|
||||||
|
private:
|
||||||
|
static index_t* fetch_block_index (Block::raw_t* in_ptr);
|
||||||
|
static void set_block_indices (Block* block, index_t new_index);
|
||||||
|
static void set_debug_signatures (Block* block);
|
||||||
|
|
||||||
|
std::vector<Block*> m_block_list;
|
||||||
|
std::unique_ptr<Block> m_head;
|
||||||
|
index_t m_prev_index;
|
||||||
|
index_t m_curr_index{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, template <class> typename A>
|
||||||
|
constexpr auto SmallObjectAllocator<T, A>::allocate (size_type size) -> pointer {
|
||||||
|
assert(object_size == size);
|
||||||
|
#if defined(NDEBUG)
|
||||||
|
static_cast<void>(size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
assert(0 == m_curr_index or m_curr_index <= m_block_list.size());
|
||||||
|
assert(m_head or m_block_list.empty());
|
||||||
|
if (!m_curr_index or !~m_block_list[m_curr_index-1]->freelist) {
|
||||||
|
A<Block> alloc_function{};
|
||||||
|
auto new_block = alloc_function();
|
||||||
|
set_debug_signatures(new_block.get());
|
||||||
|
new_block->next = std::move(m_head);
|
||||||
|
m_head.swap(new_block);
|
||||||
|
m_block_list.push_back(m_head.get());
|
||||||
|
m_prev_index = m_curr_index;
|
||||||
|
m_curr_index = static_cast<index_t>(m_block_list.size());
|
||||||
|
|
||||||
|
if (m_head->next)
|
||||||
|
set_block_indices(m_head->next.get(), m_curr_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Block*const block = m_block_list[m_curr_index-1];
|
||||||
|
assert(block != nullptr);
|
||||||
|
const uint_freelist_t neg_freelist = static_cast<uint_freelist_t>(~block->freelist);
|
||||||
|
assert(neg_freelist != 0);
|
||||||
|
|
||||||
|
const unsigned int object_num = implem::ffs(neg_freelist);
|
||||||
|
assert(object_num);
|
||||||
|
const unsigned int object_index = object_num - 1;
|
||||||
|
assert(object_index < objects_per_block);
|
||||||
|
constexpr uint_freelist_t one = 1;
|
||||||
|
block->freelist |= one << object_index;
|
||||||
|
|
||||||
|
index_t* const index_ptr = fetch_block_index(block->data + object_index);
|
||||||
|
assert(index_ptr);
|
||||||
|
*index_ptr = m_prev_index;
|
||||||
|
return reinterpret_cast<pointer>(block->data + object_index);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, template <class> typename A>
|
||||||
|
constexpr void SmallObjectAllocator<T, A>::deallocate (pointer ptr, size_type size) {
|
||||||
|
assert(object_size == size);
|
||||||
|
auto* const block_ptr = reinterpret_cast<Block::raw_t*>(ptr);
|
||||||
|
const index_t owner_prev_index = *fetch_block_index(block_ptr);
|
||||||
|
Block* const owner = (0 == owner_prev_index ? m_head.get() : m_block_list[owner_prev_index-1]->next.get());
|
||||||
|
|
||||||
|
assert(owner);
|
||||||
|
const auto flag_index = std::distance(owner->data, block_ptr);
|
||||||
|
assert(flag_index >= 0);
|
||||||
|
assert(flag_index < objects_per_block);
|
||||||
|
|
||||||
|
constexpr uint_freelist_t one = 1;
|
||||||
|
const uint_freelist_t mask = one << flag_index;
|
||||||
|
assert((owner->freelist & mask) != 0);
|
||||||
|
owner->freelist &= ~mask;
|
||||||
|
|
||||||
|
if (0 == owner->freelist) {
|
||||||
|
if (owner_prev_index) {
|
||||||
|
Block& prev = *m_block_list[owner_prev_index-1];
|
||||||
|
auto found = std::find(m_block_list.begin(), m_block_list.end(), owner);
|
||||||
|
assert(m_block_list.end() != found);
|
||||||
|
it's all fucked up here, I can't delete the item because that would
|
||||||
|
invalidate all the subsequent indices. It means all blocks following
|
||||||
|
the one being deleted would have to get set_block_indices() invoked
|
||||||
|
on them which is insane so this whole approach is fucke'
|
||||||
|
|
||||||
|
auto empty_block = std::move(prev.next);
|
||||||
|
assert(!prev.next);
|
||||||
|
prev.next = std::move(empty_block->next);
|
||||||
|
set_block_indices(prev.next.get(), owner_prev_index);
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert(m_block_list.empty());
|
||||||
|
m_head.reset();
|
||||||
|
m_curr_index = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, template <class> typename A>
|
||||||
|
auto SmallObjectAllocator<T, A>::fetch_block_index (Block::raw_t* in_ptr) -> index_t* {
|
||||||
|
assert(in_ptr);
|
||||||
|
|
||||||
|
char* ptr = reinterpret_cast<char*>(in_ptr);
|
||||||
|
#if defined(DEBUG_SMALL_OBJECT_ALLOCATOR)
|
||||||
|
ptr = align_ptr_to<alignof(std::uint32_t)>(ptr, object_size);
|
||||||
|
{
|
||||||
|
std::uint32_t read_signature;
|
||||||
|
std::memcpy(&read_signature, ptr, sizeof(signature));
|
||||||
|
assert(signature == read_signature);
|
||||||
|
}
|
||||||
|
ptr = align_ptr_to<alignof(index_t)>(ptr, sizeof(signature));
|
||||||
|
#else
|
||||||
|
ptr = align_ptr_to<alignof(index_t)>(ptr, object_size);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
index_t* const retval = reinterpret_cast<index_t*>(ptr);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, template <class> typename A>
|
||||||
|
void SmallObjectAllocator<T, A>::set_block_indices (Block* block, index_t new_index) {
|
||||||
|
uint_freelist_t flag = 1;
|
||||||
|
for (unsigned int z = 0; z < objects_per_block; ++z, flag<<=1) {
|
||||||
|
if (flag & block->freelist) {
|
||||||
|
index_t* const dst_index = fetch_block_index(block->data + z);
|
||||||
|
assert(dst_index);
|
||||||
|
*dst_index = new_index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T, template <class> typename A>
|
||||||
|
void SmallObjectAllocator<T, A>::set_debug_signatures (Block* block) {
|
||||||
|
#if defined(DEBUG_SMALL_OBJECT_ALLOCATOR)
|
||||||
|
const auto sig = signature;
|
||||||
|
for (unsigned int z = 0; z < objects_per_block; ++z) {
|
||||||
|
std::memcpy(align_ptr_to<alignof(std::uint32_t)>(block->data + z, object_size), &sig, sizeof(signature));
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static_cast<void>(block);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
} //namespace dhandy
|
||||||
|
|
||||||
|
#endif
|
152
include/duckhandy/tree_iterator.hpp
Normal file
152
include/duckhandy/tree_iterator.hpp
Normal file
|
@ -0,0 +1,152 @@
|
||||||
|
/* Copyright 2016-2024 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* "duckhandy" is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef id6109D5EDE99D43C4909F084A231BF2C2
|
||||||
|
#define id6109D5EDE99D43C4909F084A231BF2C2
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <cstddef>
|
||||||
|
#include <stack>
|
||||||
|
#include <type_traits>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
namespace dhandy {
|
||||||
|
namespace implem {
|
||||||
|
template <typename P>
|
||||||
|
class TreeIteratorStack : public std::stack<P, std::vector<P>> {
|
||||||
|
public:
|
||||||
|
void reserve (std::size_t size) {
|
||||||
|
std::stack<P, std::vector<P>>::c.reserve(size);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t capacity() const {
|
||||||
|
return std::stack<P, std::vector<P>>::c.capacity();
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename P>
|
||||||
|
class TreeIterator_base {
|
||||||
|
template <typename P1> friend class TreeIterator_base;
|
||||||
|
public:
|
||||||
|
explicit TreeIterator_base ( void ) {}
|
||||||
|
TreeIterator_base ( const TreeIterator_base& parOther );
|
||||||
|
template <typename P1>
|
||||||
|
explicit TreeIterator_base ( const TreeIterator_base<P1>& parOther );
|
||||||
|
~TreeIterator_base ( void ) {}
|
||||||
|
protected:
|
||||||
|
typedef TreeIteratorStack<P> StackType;
|
||||||
|
typedef size_t size_type;
|
||||||
|
typedef ptrdiff_t difference_type;
|
||||||
|
bool Exhausted ( void ) const;
|
||||||
|
StackType m_stack;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename N, bool Const>
|
||||||
|
class TreeIterator_const_layer;
|
||||||
|
template <typename T, typename N>
|
||||||
|
class TreeIterator_const_layer<T, N, true> : protected TreeIterator_base<const N*> {
|
||||||
|
template <typename T1, typename N1, bool Const1> friend class TreeIterator_const_layer;
|
||||||
|
typedef TreeIterator_base<const N*> parent_type;
|
||||||
|
public:
|
||||||
|
typedef const T* pointer;
|
||||||
|
typedef const T* const_pointer;
|
||||||
|
typedef const T& reference;
|
||||||
|
typedef const T& const_reference;
|
||||||
|
typedef typename parent_type::size_type size_type;
|
||||||
|
typedef typename parent_type::difference_type difference_type;
|
||||||
|
typedef N NodeType;
|
||||||
|
typedef const N* NodeTypePointer;
|
||||||
|
enum { IS_CONST = 1 };
|
||||||
|
TreeIterator_const_layer ( void ) {}
|
||||||
|
TreeIterator_const_layer ( const TreeIterator_const_layer& parOther ) : parent_type(parOther) {}
|
||||||
|
template <typename T1, bool C1>
|
||||||
|
explicit TreeIterator_const_layer ( const TreeIterator_const_layer<T1, N, C1>& parOther ) : parent_type(parOther) {}
|
||||||
|
const_reference operator* ( void ) const;
|
||||||
|
const_pointer operator-> ( void ) const;
|
||||||
|
const N* GetPointer ( void ) const;
|
||||||
|
protected:
|
||||||
|
typedef typename parent_type::StackType StackType;
|
||||||
|
};
|
||||||
|
template <typename T, typename N>
|
||||||
|
class TreeIterator_const_layer<T, N, false> : protected TreeIterator_base<N*> {
|
||||||
|
template <typename T1, typename N1, bool Const1> friend class TreeIterator_const_layer;
|
||||||
|
typedef TreeIterator_base<N*> parent_type;
|
||||||
|
public:
|
||||||
|
typedef T* pointer;
|
||||||
|
typedef const T* const_pointer;
|
||||||
|
typedef T& reference;
|
||||||
|
typedef const T& const_reference;
|
||||||
|
typedef typename parent_type::size_type size_type;
|
||||||
|
typedef typename parent_type::difference_type difference_type;
|
||||||
|
typedef N NodeType;
|
||||||
|
typedef N* NodeTypePointer;
|
||||||
|
enum { IS_CONST = 0 };
|
||||||
|
TreeIterator_const_layer ( void ) {}
|
||||||
|
TreeIterator_const_layer ( const TreeIterator_const_layer& parOther ) : parent_type(parOther) {}
|
||||||
|
template <typename T1, bool C1>
|
||||||
|
explicit TreeIterator_const_layer ( const TreeIterator_const_layer<T1, N, C1>& parOther ) : parent_type(parOther) {}
|
||||||
|
reference operator* ( void );
|
||||||
|
const_reference operator* ( void ) const;
|
||||||
|
pointer operator-> ( void );
|
||||||
|
const_pointer operator-> ( void ) const;
|
||||||
|
const N* GetPointer ( void ) const;
|
||||||
|
N* GetPointer ( void );
|
||||||
|
protected:
|
||||||
|
typedef typename parent_type::StackType StackType;
|
||||||
|
};
|
||||||
|
} //namespace implem
|
||||||
|
|
||||||
|
template <typename T, typename N>
|
||||||
|
class TreeIterator : public implem::TreeIterator_const_layer<T, N, std::is_const<T>::value> {
|
||||||
|
typedef implem::TreeIterator_const_layer<T, N, std::is_const<T>::value> parent_type;
|
||||||
|
typedef typename parent_type::NodeTypePointer NodeTypePointer;
|
||||||
|
typedef typename parent_type::NodeType NodeType;
|
||||||
|
typedef typename parent_type::StackType StackType;
|
||||||
|
public:
|
||||||
|
typedef T value_type;
|
||||||
|
typedef std::forward_iterator_tag iterator_category;
|
||||||
|
typedef typename parent_type::difference_type difference_type;
|
||||||
|
typedef typename parent_type::size_type size_type;
|
||||||
|
typedef typename parent_type::pointer pointer;
|
||||||
|
typedef typename parent_type::const_pointer const_pointer;
|
||||||
|
typedef typename parent_type::reference reference;
|
||||||
|
typedef typename parent_type::const_reference const_reference;
|
||||||
|
|
||||||
|
TreeIterator ( void );
|
||||||
|
TreeIterator ( const TreeIterator& parOther );
|
||||||
|
TreeIterator ( NodeTypePointer parRoot, size_type parMaxDepthHint );
|
||||||
|
template <typename S>
|
||||||
|
TreeIterator ( S parCopyStackBottomUp, size_type parStackLen, size_type parMaxDepthHint );
|
||||||
|
template <typename T1>
|
||||||
|
TreeIterator ( const TreeIterator<T1, N>& parOther );
|
||||||
|
~TreeIterator ( void );
|
||||||
|
|
||||||
|
const TreeIterator& operator= ( const TreeIterator& parOther );
|
||||||
|
bool operator== ( const TreeIterator& parOther ) const;
|
||||||
|
bool operator!= ( const TreeIterator& parOther ) const { return not (*this == parOther); }
|
||||||
|
TreeIterator& operator++ ( void ); //pre
|
||||||
|
TreeIterator operator++ ( int ); //post
|
||||||
|
|
||||||
|
private:
|
||||||
|
void RecurseLeft ( NodeTypePointer parFrom );
|
||||||
|
};
|
||||||
|
} //namespace dhandy
|
||||||
|
|
||||||
|
#include "implem/tree_iterator.inl"
|
||||||
|
|
||||||
|
#endif
|
|
@ -10,6 +10,8 @@ unit_test_prog = executable(meson.project_name(),
|
||||||
'version_test.cpp',
|
'version_test.cpp',
|
||||||
'tiger_test.cpp',
|
'tiger_test.cpp',
|
||||||
'infix_iterator.cpp',
|
'infix_iterator.cpp',
|
||||||
|
'tree_iterator_test.cpp',
|
||||||
|
'small_object_allocator_test.cpp',
|
||||||
install: false,
|
install: false,
|
||||||
dependencies: [sprout_dep, catch2_dep],
|
dependencies: [sprout_dep, catch2_dep],
|
||||||
include_directories: [public_incl],
|
include_directories: [public_incl],
|
||||||
|
|
75
test/unit/small_object_allocator_test.cpp
Normal file
75
test/unit/small_object_allocator_test.cpp
Normal file
|
@ -0,0 +1,75 @@
|
||||||
|
/* Copyright 2016-2024 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* "duckhandy" is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "catch2/catch_test_macros.hpp"
|
||||||
|
#include "duckhandy/small_object_allocator.hpp"
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
static const void* g_alloc_last{nullptr};
|
||||||
|
static std::size_t g_alloc_count{0};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct TestAlloc {
|
||||||
|
std::unique_ptr<T> operator()() {
|
||||||
|
auto ret = std::make_unique<T>();
|
||||||
|
g_alloc_last = ret.get();
|
||||||
|
++g_alloc_count;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} //unnamed namespace
|
||||||
|
|
||||||
|
TEST_CASE("Check SmallObjectAllocator", "[SmallObjectAllocator][containers][memory][allocators]") {
|
||||||
|
dhandy::SmallObjectAllocator<int, TestAlloc> soa;
|
||||||
|
CHECK(g_alloc_last == nullptr);
|
||||||
|
CHECK(g_alloc_count == 0u);
|
||||||
|
|
||||||
|
int* const int01 = soa.allocate(sizeof(int));
|
||||||
|
const void* old_block = g_alloc_last;
|
||||||
|
REQUIRE(int01 != nullptr);
|
||||||
|
CHECK(g_alloc_last != nullptr);
|
||||||
|
CHECK(g_alloc_count == 1);
|
||||||
|
|
||||||
|
int* const int02 = soa.allocate(sizeof(int));
|
||||||
|
REQUIRE(int02 != nullptr);
|
||||||
|
CHECK(int01 != int02);
|
||||||
|
CHECK(g_alloc_last == old_block);
|
||||||
|
CHECK(g_alloc_count == 1);
|
||||||
|
|
||||||
|
int* prev_int = int02;
|
||||||
|
for (std::size_t z = 2; z < dhandy::SmallObjectAllocator<int, TestAlloc>::objects_per_block; ++z) {
|
||||||
|
int* const new_int = soa.allocate(sizeof(int));
|
||||||
|
REQUIRE(new_int != nullptr);
|
||||||
|
CHECK(new_int != int01);
|
||||||
|
CHECK(new_int != prev_int);
|
||||||
|
prev_int = new_int;
|
||||||
|
CHECK(g_alloc_last == old_block);
|
||||||
|
CHECK(g_alloc_count == 1);
|
||||||
|
CHECK(reinterpret_cast<std::uintptr_t>(new_int) % alignof(int) == 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int* const int03 = soa.allocate(sizeof(int));
|
||||||
|
REQUIRE(int03 != nullptr);
|
||||||
|
CHECK(int03 != int02);
|
||||||
|
CHECK(int03 != int01);
|
||||||
|
CHECK(g_alloc_last != old_block);
|
||||||
|
old_block = g_alloc_last;
|
||||||
|
CHECK(g_alloc_count == 2);
|
||||||
|
|
||||||
|
soa.deallocate(int02, sizeof(int));
|
||||||
|
}
|
136
test/unit/tree_iterator_test.cpp
Normal file
136
test/unit/tree_iterator_test.cpp
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
/* Copyright 2016-2024 Michele Santullo
|
||||||
|
* This file is part of "duckhandy".
|
||||||
|
*
|
||||||
|
* "duckhandy" is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* "duckhandy" is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with "duckhandy". If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "catch2/catch_test_macros.hpp"
|
||||||
|
#include "duckhandy/tree_iterator.hpp"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
enum Letters : unsigned int {
|
||||||
|
A = 'a', B, C, D, E, F, G, H, I
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TestNode {
|
||||||
|
TestNode (unsigned int val) : content(val) {}
|
||||||
|
TestNode* left{nullptr}, *right{nullptr};
|
||||||
|
unsigned int content{0};
|
||||||
|
};
|
||||||
|
} //unnamed namespace
|
||||||
|
|
||||||
|
TEST_CASE("Check TreeIterator", "[TreeIterator][containers][iterator]") {
|
||||||
|
typedef dhandy::TreeIterator<unsigned int, TestNode> TestIterator;
|
||||||
|
|
||||||
|
TestIterator empty;
|
||||||
|
|
||||||
|
{
|
||||||
|
TestNode root{0xDEADBEEFu};
|
||||||
|
TestIterator it{&root, 1};
|
||||||
|
CHECK(*it == 0xDEADBEEFu);
|
||||||
|
|
||||||
|
++it;
|
||||||
|
CHECK(it == empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TestNode root{A};
|
||||||
|
TestNode l{B}, ll{C}, lr{D};
|
||||||
|
TestNode r{E}, rl{F}, rr{G}, rll{H}, rrr{I};
|
||||||
|
|
||||||
|
root.left = &l;
|
||||||
|
root.right = &r;
|
||||||
|
l.left = ≪
|
||||||
|
l.right = &lr;
|
||||||
|
r.left = &rl;
|
||||||
|
r.right = &rr;
|
||||||
|
rl.left = &rll;
|
||||||
|
rr.right = &rrr;
|
||||||
|
|
||||||
|
TestIterator it{&root, 4};
|
||||||
|
CHECK(*it == C); //ll
|
||||||
|
++it;
|
||||||
|
CHECK(*it == B); //l
|
||||||
|
it++;
|
||||||
|
CHECK(*it == D); //lr
|
||||||
|
it++;
|
||||||
|
CHECK(*it == A); //root
|
||||||
|
++it;
|
||||||
|
CHECK(*it == H); //rll
|
||||||
|
it++;
|
||||||
|
CHECK(*it == F); //rl
|
||||||
|
it++;
|
||||||
|
CHECK(*it == E); //r
|
||||||
|
++it;
|
||||||
|
CHECK(*it == G); //rr
|
||||||
|
++it;
|
||||||
|
CHECK(*it == I); //rrr
|
||||||
|
CHECK(it != empty);
|
||||||
|
it++;
|
||||||
|
CHECK(it == empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TestNode root{A};
|
||||||
|
TestNode l{B}, ll{C}, lll{D}, llll{E}, lllll{F};
|
||||||
|
root.left = &l;
|
||||||
|
l.left = ≪
|
||||||
|
ll.left = &lll;
|
||||||
|
lll.left = &llll;
|
||||||
|
llll.left = &lllll;
|
||||||
|
|
||||||
|
TestIterator it{&root, 6};
|
||||||
|
CHECK(it != empty);
|
||||||
|
CHECK(*it == F);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == E);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == D);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == C);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == B);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == A);
|
||||||
|
++it;
|
||||||
|
CHECK(it == empty);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
TestNode root{A};
|
||||||
|
TestNode r{B}, rr{C}, rrr{D}, rrrr{E}, rrrrr{F};
|
||||||
|
root.right = &r;
|
||||||
|
r.right = &rr;
|
||||||
|
rr.right = &rrr;
|
||||||
|
rrr.right = &rrrr;
|
||||||
|
rrrr.right = &rrrrr;
|
||||||
|
|
||||||
|
TestIterator it{&root, 6};
|
||||||
|
CHECK(it != empty);
|
||||||
|
CHECK(*it == A);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == B);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == C);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == D);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == E);
|
||||||
|
++it;
|
||||||
|
CHECK(*it == F);
|
||||||
|
++it;
|
||||||
|
CHECK(it == empty);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue