From 5c4cacf2866c9fe827bd3fd7050310d509993367 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sat, 23 Aug 2025 12:26:16 +0100 Subject: [PATCH 1/3] Nonworking implementation attempt I think the whole approach is wrong and this needs to be re-architected --- include/duckhandy/alignment.hpp | 48 ++++ include/duckhandy/small_object_allocator.hpp | 268 +++++++++++++++++++ 2 files changed, 316 insertions(+) create mode 100644 include/duckhandy/alignment.hpp create mode 100644 include/duckhandy/small_object_allocator.hpp diff --git a/include/duckhandy/alignment.hpp b/include/duckhandy/alignment.hpp new file mode 100644 index 0000000..aa7f610 --- /dev/null +++ b/include/duckhandy/alignment.hpp @@ -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 . + */ + +#ifndef id99DC0F782D0F4907A7768E8743A8BE74 +#define id99DC0F782D0F4907A7768E8743A8BE74 + +#include +#include +#include + +namespace dhandy { +template +concept is_pow_of_two = static_cast(V and not(V bitand (V - 1u))); + +template +requires is_pow_of_two +constexpr T align_to (T v) { + return (v + V-1) & ~(T{V-1}); +} + +template +requires is_pow_of_two +constexpr T padding_to (T v) { + return (-v) & (V-1); +} + +template +requires is_pow_of_two +constexpr T* align_ptr_to (T* ptr, std::size_t add) { + return reinterpret_cast(align_to(reinterpret_cast(ptr) + add)); +} + +} //namespace dhandy +#endif diff --git a/include/duckhandy/small_object_allocator.hpp b/include/duckhandy/small_object_allocator.hpp new file mode 100644 index 0000000..b3fe28b --- /dev/null +++ b/include/duckhandy/small_object_allocator.hpp @@ -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 . + */ + +#ifndef idE77208CAFC79452DA12757DD0F6692D3 +#define idE77208CAFC79452DA12757DD0F6692D3 + +#include "alignment.hpp" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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(::ffs(static_cast(val))); } + +[[gnu::pure,gnu::always_inline]] +unsigned int ffs (unsigned long val) { return static_cast(::ffsl(static_cast(val))); } + +[[gnu::pure,gnu::always_inline]] +unsigned int ffs (unsigned long long val) { return static_cast(::ffsll(static_cast(val))); } + +#if defined(DEBUG_SMALL_OBJECT_ALLOCATOR) +template typename A> +consteval std::uint32_t make_signature() { + return static_cast( + bt::tiger(__PRETTY_FUNCTION__, lengthof(__PRETTY_FUNCTION__), bt::TigerPaddingV2).a & 0xFFFFFFFF + ); +} +#endif +} //namespace implem + +template +struct AllocatorFunction { + std::unique_ptr operator()() { + return std::make_unique(); + } +}; + +template 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(); + 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::type raw_t; + + raw_t data[objects_per_block]; + std::unique_ptr next; + uint_freelist_t freelist{0}; + }; + +public: + + SmallObjectAllocator() = default; + ~SmallObjectAllocator() noexcept = default; + + [[nodiscard]] constexpr pointer allocate (size_type size); + //[[nodiscard]] constexpr std::allocation_result allocate_at_least (size_type size); + constexpr void deallocate (pointer ptr, size_type size); + + //template + //constexpr bool operator== (const allocator& 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 m_block_list; + std::unique_ptr m_head; + index_t m_prev_index; + index_t m_curr_index{0}; +}; + +template typename A> +constexpr auto SmallObjectAllocator::allocate (size_type size) -> pointer { + assert(object_size == size); +#if defined(NDEBUG) + static_cast(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 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(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(~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(block->data + object_index); +} + +template typename A> +constexpr void SmallObjectAllocator::deallocate (pointer ptr, size_type size) { + assert(object_size == size); + auto* const block_ptr = reinterpret_cast(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 A> +auto SmallObjectAllocator::fetch_block_index (Block::raw_t* in_ptr) -> index_t* { + assert(in_ptr); + + char* ptr = reinterpret_cast(in_ptr); +#if defined(DEBUG_SMALL_OBJECT_ALLOCATOR) + ptr = align_ptr_to(ptr, object_size); + { + std::uint32_t read_signature; + std::memcpy(&read_signature, ptr, sizeof(signature)); + assert(signature == read_signature); + } + ptr = align_ptr_to(ptr, sizeof(signature)); +#else + ptr = align_ptr_to(ptr, object_size); +#endif + + index_t* const retval = reinterpret_cast(ptr); + return retval; +} + +template typename A> +void SmallObjectAllocator::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 A> +void SmallObjectAllocator::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(block->data + z, object_size), &sig, sizeof(signature)); + } +#else + static_cast(block); +#endif +} +} //namespace dhandy + +#endif From e05386eafe5d230c2d23048101f54bb6336ea094 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sat, 23 Aug 2025 12:27:14 +0100 Subject: [PATCH 2/3] Tree iterator first commit --- include/duckhandy/implem/tree_iterator.inl | 252 +++++++++++++++++++++ include/duckhandy/tree_iterator.hpp | 152 +++++++++++++ test/unit/meson.build | 1 + test/unit/tree_iterator_test.cpp | 136 +++++++++++ 4 files changed, 541 insertions(+) create mode 100644 include/duckhandy/implem/tree_iterator.inl create mode 100644 include/duckhandy/tree_iterator.hpp create mode 100644 test/unit/tree_iterator_test.cpp diff --git a/include/duckhandy/implem/tree_iterator.inl b/include/duckhandy/implem/tree_iterator.inl new file mode 100644 index 0000000..eb2f61b --- /dev/null +++ b/include/duckhandy/implem/tree_iterator.inl @@ -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 . + */ + +namespace dhandy { + namespace implem { + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + bool TreeIterator_base

::Exhausted() const { + return m_stack.empty(); + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + TreeIterator_base

::TreeIterator_base (const TreeIterator_base& parOther) : + m_stack(parOther.m_stack) + { + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + template + TreeIterator_base

::TreeIterator_base (const TreeIterator_base& parOther) { + typename TreeIterator_base::StackType otherStackCopy(parOther.m_stack); + std::vector

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

::reverse_iterator itRev = localCopy.rbegin(), itRevEnd = localCopy.rend(); itRev != itRevEnd; ++itRev) { + assert(m_stack.capacity() > m_stack.size()); + m_stack.push(*itRev); + } + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::reference TreeIterator_const_layer::operator* () { + assert(not this->Exhausted()); + return this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::const_reference TreeIterator_const_layer::operator* () const { + assert(not this->Exhausted()); + return this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::pointer TreeIterator_const_layer::operator-> () { + assert(not this->Exhausted()); + return &this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::const_pointer TreeIterator_const_layer::operator-> () const { + assert(not this->Exhausted()); + return &this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + N* TreeIterator_const_layer::GetPointer() { + assert(not this->Exhausted()); + return this->m_stack.top(); + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + const N* TreeIterator_const_layer::GetPointer() const { + assert(not this->Exhausted()); + return this->m_stack.top(); + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::const_reference TreeIterator_const_layer::operator* () const { + assert(not this->Exhausted()); + return this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + typename TreeIterator_const_layer::const_pointer TreeIterator_const_layer::operator-> () const { + assert(not this->Exhausted()); + return &this->m_stack.top()->content; + } + + ///--------------------------------------------------------------------- + ///--------------------------------------------------------------------- + template + const N* TreeIterator_const_layer::GetPointer() const { + assert(not this->Exhausted()); + return this->m_stack.top(); + } + } //namespace implem + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + TreeIterator::TreeIterator() { + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + TreeIterator::TreeIterator (const TreeIterator& parOther) : + parent_type(parOther) + { + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + template + TreeIterator::TreeIterator (const TreeIterator& parOther) : + parent_type(parOther) + { + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + template + TreeIterator::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 + TreeIterator::TreeIterator (NodeTypePointer parRoot, size_type parMaxDepthHint) { + if (parMaxDepthHint > 0) + this->m_stack.reserve(parMaxDepthHint); + RecurseLeft(parRoot); + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + TreeIterator::~TreeIterator() { + } + + ///------------------------------------------------------------------------- + ///Post-increment + ///------------------------------------------------------------------------- + template + TreeIterator TreeIterator::operator++ (int) { + assert(not this->Exhausted()); + TreeIterator retVal = *this; + ++(*this); + return retVal; + } + + ///------------------------------------------------------------------------- + ///Pre-increment + ///------------------------------------------------------------------------- + template + TreeIterator& TreeIterator::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 + const TreeIterator& TreeIterator::operator= (const TreeIterator& parOther) { + this->m_stack = parOther.m_stack; + assert(this->m_stack.capacity() >= parOther.m_stack.capacity()); + return *this; + } + + ///------------------------------------------------------------------------- + ///------------------------------------------------------------------------- + template + bool TreeIterator::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 + void TreeIterator::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 diff --git a/include/duckhandy/tree_iterator.hpp b/include/duckhandy/tree_iterator.hpp new file mode 100644 index 0000000..40b3e41 --- /dev/null +++ b/include/duckhandy/tree_iterator.hpp @@ -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 . + */ + +#ifndef id6109D5EDE99D43C4909F084A231BF2C2 +#define id6109D5EDE99D43C4909F084A231BF2C2 + +#include +#include +#include +#include +#include + +namespace dhandy { + namespace implem { + template + class TreeIteratorStack : public std::stack> { + public: + void reserve (std::size_t size) { + std::stack>::c.reserve(size); + } + + std::size_t capacity() const { + return std::stack>::c.capacity(); + } + private: + }; + + template + class TreeIterator_base { + template friend class TreeIterator_base; + public: + explicit TreeIterator_base ( void ) {} + TreeIterator_base ( const TreeIterator_base& parOther ); + template + explicit TreeIterator_base ( const TreeIterator_base& parOther ); + ~TreeIterator_base ( void ) {} + protected: + typedef TreeIteratorStack

StackType; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + bool Exhausted ( void ) const; + StackType m_stack; + }; + + template + class TreeIterator_const_layer; + template + class TreeIterator_const_layer : protected TreeIterator_base { + template friend class TreeIterator_const_layer; + typedef TreeIterator_base 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 + explicit TreeIterator_const_layer ( const TreeIterator_const_layer& 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 + class TreeIterator_const_layer : protected TreeIterator_base { + template friend class TreeIterator_const_layer; + typedef TreeIterator_base 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 + explicit TreeIterator_const_layer ( const TreeIterator_const_layer& 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 + class TreeIterator : public implem::TreeIterator_const_layer::value> { + typedef implem::TreeIterator_const_layer::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 + TreeIterator ( S parCopyStackBottomUp, size_type parStackLen, size_type parMaxDepthHint ); + template + TreeIterator ( const TreeIterator& 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 diff --git a/test/unit/meson.build b/test/unit/meson.build index 5ec1bd0..2373f86 100644 --- a/test/unit/meson.build +++ b/test/unit/meson.build @@ -10,6 +10,7 @@ unit_test_prog = executable(meson.project_name(), 'version_test.cpp', 'tiger_test.cpp', 'infix_iterator.cpp', + 'tree_iterator_test.cpp', install: false, dependencies: [sprout_dep, catch2_dep], include_directories: [public_incl], diff --git a/test/unit/tree_iterator_test.cpp b/test/unit/tree_iterator_test.cpp new file mode 100644 index 0000000..b0533a4 --- /dev/null +++ b/test/unit/tree_iterator_test.cpp @@ -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 . + */ + +#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 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); + } +} From e3f902d6e302c6efad94a1040b6bc81c0780d612 Mon Sep 17 00:00:00 2001 From: King_DuckZ Date: Sat, 23 Aug 2025 14:26:11 +0100 Subject: [PATCH 3/3] Unit test for SmallObjectAllocator --- test/unit/meson.build | 1 + test/unit/small_object_allocator_test.cpp | 75 +++++++++++++++++++++++ 2 files changed, 76 insertions(+) create mode 100644 test/unit/small_object_allocator_test.cpp diff --git a/test/unit/meson.build b/test/unit/meson.build index 5086fca..9fa0534 100644 --- a/test/unit/meson.build +++ b/test/unit/meson.build @@ -11,6 +11,7 @@ unit_test_prog = executable(meson.project_name(), 'tiger_test.cpp', 'infix_iterator.cpp', 'tree_iterator_test.cpp', + 'small_object_allocator_test.cpp', install: false, dependencies: [sprout_dep, catch2_dep], include_directories: [public_incl], diff --git a/test/unit/small_object_allocator_test.cpp b/test/unit/small_object_allocator_test.cpp new file mode 100644 index 0000000..a7258ab --- /dev/null +++ b/test/unit/small_object_allocator_test.cpp @@ -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 . + */ + +#include "catch2/catch_test_macros.hpp" +#include "duckhandy/small_object_allocator.hpp" +#include + +namespace { +static const void* g_alloc_last{nullptr}; +static std::size_t g_alloc_count{0}; + +template +struct TestAlloc { + std::unique_ptr operator()() { + auto ret = std::make_unique(); + g_alloc_last = ret.get(); + ++g_alloc_count; + return ret; + } +}; +} //unnamed namespace + +TEST_CASE("Check SmallObjectAllocator", "[SmallObjectAllocator][containers][memory][allocators]") { + dhandy::SmallObjectAllocator 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::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(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)); +}