#pragma once #include "RE/B/BSTTuple.h" namespace RE::Impl { template concept transparent_comparator = requires( const K& a_transparent, const typename C::key_type& a_key, typename C::key_compare& a_compare) { typename C::key_compare::is_transparent; // clang-format off { a_compare(a_transparent, a_key) } -> std::convertible_to; { a_compare(a_key, a_transparent) } -> std::convertible_to; // clang-format on }; } namespace RE { template < class Key, class T, class Compare = std::less<>> class BSTBTree { public: using key_type = Key; using mapped_type = T; using value_type = BSTTuple; using size_type = std::uint32_t; using key_compare = Compare; static_assert(std::is_empty_v, "compare object must be stateless"); // traverse entries using a leftmost binary search // index nodes with with index of lower bound, iff entry is not found at this level // nodes are always allocated as powers of 2 // maybe std::bit_ceil(sizeof(node_type)) ? struct node_type { public: using iterator = value_type*; using const_iterator = const value_type*; [[nodiscard]] iterator begin() noexcept { return entries; } [[nodiscard]] const_iterator begin() const noexcept { return entries; } [[nodiscard]] const_iterator cbegin() const noexcept { return entries; } [[nodiscard]] iterator end() noexcept { return entries + usedEntries; } [[nodiscard]] const_iterator end() const noexcept { return entries + usedEntries; } [[nodiscard]] const_iterator cend() const noexcept { return entries + usedEntries; } // members value_type entries[4]; // 00 node_type* nodes[5]{ nullptr }; // ?? size_type usedEntries{ 0 }; // ?? }; private: template class iterator_base { public: using difference_type = std::ptrdiff_t; using value_type = std::remove_const_t; using pointer = value_type*; using reference = value_type&; using iterator_category = std::forward_iterator_tag; iterator_base() noexcept = default; template iterator_base(const iterator_base& a_rhs) // requires(std::convertible_to::reference, reference>) : _queued(a_rhs._queued), _cur(a_rhs._cur), _pos(a_rhs._pos) {} template iterator_base(iterator_base&& a_rhs) noexcept // requires(std::convertible_to::reference, reference>) : _queued(std::move(a_rhs._queued)), _cur(std::exchange(a_rhs._cur, nullptr)), _pos(std::exchange(a_rhs._pos, 0)) {} ~iterator_base() noexcept = default; template iterator_base& operator=(const iterator_base& a_rhs) // requires(std::convertible_to::reference, reference>) { if (this != std::addressof(a_rhs)) { _queued = a_rhs._queued; _cur = a_rhs._cur; _pos = a_rhs._pos; } return *this; } template iterator_base& operator=(iterator_base&& a_rhs) noexcept // requires(std::convertible_to::reference, reference>) { if (this != std::addressof(a_rhs)) { _queued = std::move(a_rhs._queued); _cur = std::exchange(a_rhs._cur, nullptr); _pos = std::exchange(a_rhs._pos, 0); } return *this; } [[nodiscard]] reference operator*() const noexcept { assert(_cur != nullptr); assert(_pos < _cur->usedEntries); return _cur->entries[_pos]; } [[nodiscard]] pointer operator->() const noexcept { return std::pointer_traits::pointer_to(operator*()); } template [[nodiscard]] bool operator==(const iterator_base& a_rhs) const noexcept { return _cur == nullptr && a_rhs._cur == nullptr; } template [[nodiscard]] bool operator!=(const iterator_base& a_rhs) const noexcept { return !operator==(a_rhs); } iterator_base& operator++() noexcept { assert(_cur != nullptr); if (++_pos >= _cur->usedEntries) { _cur = nullptr; _pos = 0; if (!_queued.empty()) { _cur = _queued.top(); _queued.pop(); push_level(); } } return *this; } iterator_base operator++(int) noexcept { iterator_base tmp{ *this }; operator++(); return tmp; } protected: template friend class BSTBTree; explicit iterator_base(node_type* a_node) noexcept : iterator_base(a_node, 0) {} iterator_base(node_type* a_node, size_type a_pos) noexcept : _pos(a_pos) { if (a_node && a_node->usedEntries > 0) { _cur = a_node; push_level(); } } private: template friend class iterator_base; void push_level() { for (size_type i = 0; i <= _cur->usedEntries; ++i) { if (const auto node = _cur->nodes[i]; node && node->usedEntries > 0) { _queued.push(node); } } } std::stack _queued; node_type* _cur{ nullptr }; size_type _pos{ 0 }; }; public: using iterator = iterator_base; using const_iterator = iterator_base; [[nodiscard]] iterator begin() noexcept { return iterator{ _root }; } [[nodiscard]] const_iterator begin() const noexcept { return const_iterator{ _root }; } [[nodiscard]] const_iterator cbegin() const noexcept { return const_iterator{ _root }; } [[nodiscard]] iterator end() noexcept { return {}; } [[nodiscard]] const_iterator end() const noexcept { return {}; } [[nodiscard]] const_iterator cend() const noexcept { return {}; } [[nodiscard]] iterator find(const key_type& a_key) { return do_find(a_key); } [[nodiscard]] const_iterator find(const key_type& a_key) const { return do_find(a_key); } template [[nodiscard]] iterator find(const K& a_key) // requires(Impl::transparent_comparator) { return do_find(a_key); } template [[nodiscard]] const_iterator find(const K& a_key) const // requires(Impl::transparent_comparator) { return do_find(a_key); } [[nodiscard]] key_compare key_comp() const { return key_compare{}; } private: template [[nodiscard]] Iter do_find(const K& a_key) const { auto node = _root; while (node) { const auto it = std::lower_bound( node->begin(), node->end(), a_key, [&](const value_type& a_lhs, const key_type& a_rhs) { return key_comp()(a_lhs.first, a_rhs); }); if (it != node->end()) { const auto pos = static_cast(it - node->begin()); if (!key_comp()(a_key, it->first)) { return Iter(node, pos); } else { node = node->nodes[pos]; } } else { node = node->nodes[node->usedEntries]; } } return Iter(); } std::uint64_t pad{ 0 }; // 00 size_type _activeEntry{ 0 }; // 08 size_type _allocatedSize{ 0 }; // 0C node_type* _root{ nullptr }; // 10 node_type* _availNodes{ nullptr }; // 18 }; }