From a26df87bcf71619756fd75899a13cf89612bf9c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Wed, 11 Feb 2026 11:10:18 +0100 Subject: [PATCH] =?UTF-8?q?Added,=20for=20testing=20and=20comparison=20pur?= =?UTF-8?q?poses,=20experimental=20"nest"=20container=20under=20"experimen?= =?UTF-8?q?tal"=20folder,=20a=20C++03=20backport=20and=20adaptation=20to?= =?UTF-8?q?=20use=20Boost.Container=20internals=20of=20Joaqu=C3=ADn=20L?= =?UTF-8?q?=C3=B3pez=20Mu=C3=B1oz's=20"hub"=20container=20proposal=20(http?= =?UTF-8?q?s://lists.boost.org/archives/list/boost@lists.boost.org/message?= =?UTF-8?q?/KS67XSLDY64Q37BDFUAEUXHTQSUL6HC7/).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- include/boost/container/experimental/nest.hpp | 2464 +++++++++++++++++ .../boost/container/experimental/pmr/nest.hpp | 45 + test/CMakeLists.txt | 2 + test/nest_test.cpp | 337 +++ test/pmr_nest_test.cpp | 28 + 5 files changed, 2876 insertions(+) create mode 100644 include/boost/container/experimental/nest.hpp create mode 100644 include/boost/container/experimental/pmr/nest.hpp create mode 100644 test/nest_test.cpp create mode 100644 test/pmr_nest_test.cpp diff --git a/include/boost/container/experimental/nest.hpp b/include/boost/container/experimental/nest.hpp new file mode 100644 index 0000000..2640769 --- /dev/null +++ b/include/boost/container/experimental/nest.hpp @@ -0,0 +1,2464 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Joaquin M Lopez Munoz 2025-2026. +// (C) Copyright Ion Gaztanaga 2025-2026. +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// +#ifndef BOOST_CONTAINER_EXPERIMENTAL_NEST_HPP +#define BOOST_CONTAINER_EXPERIMENTAL_NEST_HPP + +#ifndef BOOST_CONFIG_HPP +# include +#endif + +#if defined(BOOST_HAS_PRAGMA_ONCE) +# pragma once +#endif + +#include +#include + +// container +#include +#include +#include +// container/detail +#include +#include +#include +#include +#include +// move +#include +#include +#include +#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) +# include +#endif +#include +#include +// intrusive +#include +// core +#include +#include +#include +#include +#include +// std +#include +#include +#include +#include +#include + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) +#include +#endif + +#if !defined(BOOST_CONTAINER_NEST_DISABLE_SSE2) +#if defined(BOOST_CONTAINER_NEST_ENABLE_SSE2)|| \ + defined(__SSE2__) || \ + defined(_M_X64) || (defined(_M_IX86_FP)&&_M_IX86_FP>=2) +#define BOOST_CONTAINER_NEST_SSE2 +#endif +#endif + +#if defined(BOOST_CONTAINER_NEST_SSE2) +#include +#endif + +#ifdef __has_builtin +#define BOOST_CONTAINER_NEST_HAS_BUILTIN(x) __has_builtin(x) +#else +#define BOOST_CONTAINER_NEST_HAS_BUILTIN(x) 0 +#endif + +#if !defined(NDEBUG) +#define BOOST_CONTAINER_NEST_ASSUME(cond) BOOST_ASSERT(cond) +#elif BOOST_CONTAINER_NEST_HAS_BUILTIN(__builtin_assume) +#define BOOST_CONTAINER_NEST_ASSUME(cond) __builtin_assume(cond) +#elif defined(__GNUC__) || \ + BOOST_CONTAINER_NEST_HAS_BUILTIN(__builtin_unreachable) +#define BOOST_CONTAINER_NEST_ASSUME(cond) \ + do{ \ + if(!(cond)) __builtin_unreachable(); \ + } while(0) +#elif defined(_MSC_VER) +#define BOOST_CONTAINER_NEST_ASSUME(cond) __assume(cond) +#else +#define BOOST_CONTAINER_NEST_ASSUME(cond) \ + do{ \ + static_cast(false && (cond)); \ + } while(0) +#endif + +/* We use BOOST_CONTAINER_NEST_PREFETCH[_BLOCK] macros rather than proper + * functions because of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=109985 + */ + +#if defined(BOOST_GCC) || defined(BOOST_CLANG) +#define BOOST_CONTAINER_NEST_PREFETCH(p) \ +__builtin_prefetch(static_cast(static_cast(boost::movelib::to_raw_pointer(p)))) +#elif defined(BOOST_CONTAINER_NEST_SSE2) +#define BOOST_CONTAINER_NEST_PREFETCH(p) \ +_mm_prefetch(static_cast(static_cast(boost::movelib::to_raw_pointer(p))), _MM_HINT_T0) +#else +#define BOOST_CONTAINER_NEST_PREFETCH(p) ((void)(p)) +#endif + +#define BOOST_CONTAINER_NEST_PREFETCH_BLOCK(pbb, Block) \ +do{ \ + Block *p0_ = &static_cast(*(pbb)); \ + BOOST_CONTAINER_NEST_PREFETCH(p0_->data()); \ +} while(0) + +#if defined(BOOST_MSVC) +#pragma warning(push) +#pragma warning(disable:4714) /* marked as __forceinline not inlined */ +#endif + +namespace boost { +namespace container { + +#ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + +namespace nest_detail { + +////////////////////////////////////////////// +// +// pointer_rebind helper +// +////////////////////////////////////////////// + +template +struct pointer_rebind +{ + typedef typename boost::intrusive::pointer_traits:: + template rebind_pointer::type type; +}; + +////////////////////////////////////////////// +// +// bit manipulation helpers +// +////////////////////////////////////////////// + +inline int unchecked_countr_zero(boost::uint64_t x) +{ +#if defined(BOOST_MSVC) && (defined(_M_X64) || defined(_M_ARM64)) + unsigned long r; + _BitScanForward64(&r, x); + return (int)r; +#elif defined(BOOST_GCC) || defined(BOOST_CLANG) + return (int)__builtin_ctzll(x); +#else + BOOST_CONTAINER_NEST_ASSUME(x != 0); + return (int)boost::core::countr_zero(x); +#endif +} + +inline int unchecked_countr_one(boost::uint64_t x) +{ + return unchecked_countr_zero(~x); +} + +inline int unchecked_countl_zero(boost::uint64_t x) +{ +#if defined(BOOST_MSVC) && (defined(_M_X64) || defined(_M_ARM64)) + unsigned long r; + _BitScanReverse64(&r, x); + return (int)(63 - r); +#elif defined(BOOST_GCC) || defined(BOOST_CLANG) + return (int)__builtin_clzll(x); +#else + BOOST_CONTAINER_NEST_ASSUME(x != 0); + return (int)boost::core::countl_zero(x); +#endif +} + +////////////////////////////////////////////// +// +// find_if_not (C++03-compatible) +// +////////////////////////////////////////////// + +template +It find_if_not(It first, It last, Pred pred) +{ + for (; first != last; ++first) + if (!pred(*first)) + break; + return first; +} + +////////////////////////////////////////////// +// +// block_base / block / block_list +// +////////////////////////////////////////////// + +template +struct block_base +{ + typedef typename pointer_rebind::type pointer; + typedef typename pointer_rebind::type const_pointer; + typedef boost::uint64_t mask_type; + + BOOST_STATIC_CONSTEXPR std::size_t N = 64; + BOOST_STATIC_CONSTEXPR mask_type full = (mask_type)(-1); + + static pointer pointer_to(block_base& x) BOOST_NOEXCEPT + { + return boost::intrusive::pointer_traits::pointer_to(x); + } + + static const_pointer pointer_to(const block_base& x) BOOST_NOEXCEPT + { + return boost::intrusive::pointer_traits::pointer_to(x); + } + + BOOST_CONTAINER_FORCEINLINE void link_available_before(pointer p) BOOST_NOEXCEPT + { + next_available = p; + prev_available = p->prev_available; + next_available->prev_available = pointer_to(*this); + prev_available->next_available = pointer_to(*this); + } + + BOOST_CONTAINER_FORCEINLINE void link_available_after(pointer p) BOOST_NOEXCEPT + { + prev_available = p; + next_available = p->next_available; + next_available->prev_available = pointer_to(*this); + prev_available->next_available = pointer_to(*this); + } + + BOOST_CONTAINER_FORCEINLINE void unlink_available() BOOST_NOEXCEPT + { + prev_available->next_available = next_available; + next_available->prev_available = prev_available; + } + + BOOST_CONTAINER_FORCEINLINE void link_before(pointer p) BOOST_NOEXCEPT + { + next = p; + prev = p->prev; + next->prev = pointer_to(*this); + prev->next = pointer_to(*this); + } + + BOOST_CONTAINER_FORCEINLINE void link_after(pointer p) BOOST_NOEXCEPT + { + prev = p; + next = p->next; + next->prev = pointer_to(*this); + prev->next = pointer_to(*this); + } + + BOOST_CONTAINER_FORCEINLINE void unlink() BOOST_NOEXCEPT + { + prev->next = next; + next->prev = prev; + } + + pointer prev_available; + pointer next_available; + pointer prev; + pointer next; + mask_type mask; +}; + +template +struct block + : block_base::type> +{ + typedef block_base::type> super; + + ValuePointer data() BOOST_NOEXCEPT { return data_; } + ValuePointer data_; +}; + +template +void swap_payload(block& x, block& y) BOOST_NOEXCEPT +{ + boost::adl_move_swap(x.mask, y.mask); + boost::adl_move_swap(x.data_, y.data_); +} + +template +struct block_list + : block +{ + typedef nest_detail::block block_type; + typedef typename block_type::super block_base_type; + typedef typename block_base_type::pointer block_base_pointer; + typedef typename block_base_type::const_pointer const_block_base_pointer; + typedef typename pointer_rebind::type block_pointer; + + // bring base names into scope + using block_base_type::full; + using block_base_type::prev_available; + using block_base_type::next_available; + using block_base_type::prev; + using block_base_type::next; + using block_base_type::mask; + using block_type::data_; + + static block_pointer + static_cast_block_pointer(block_base_pointer pbb) BOOST_NOEXCEPT + { + return boost::intrusive::pointer_traits::pointer_to( + static_cast(*pbb)); + } + + block_list() + { + reset(); + mask = 1; /* sentinel */ + data_ = ValuePointer(); + } + + block_list(BOOST_RV_REF(block_list) x) BOOST_NOEXCEPT + { + reset(); + mask = 1; /* sentinel */ + data_ = ValuePointer(); + if(x.next_available != x.header()) { + prev_available = x.prev_available; + next_available = x.next_available; + next_available->prev_available = header(); + prev_available->next_available = header(); + } + if(x.prev != x.header()) { + prev = x.prev; + next = x.next; + next->prev = header(); + prev->next = header(); + } + x.reset(); + } + + block_list& operator=(BOOST_RV_REF(block_list) x) BOOST_NOEXCEPT + { + reset(); + if(x.next_available != x.header()) { + prev_available = x.prev_available; + next_available = x.next_available; + next_available->prev_available = header(); + prev_available->next_available = header(); + } + if(x.prev != x.header()) { + prev = x.prev; + next = x.next; + next->prev = header(); + prev->next = header(); + } + x.reset(); + return *this; + } + + void reset() BOOST_NOEXCEPT + { + prev_available = header(); + next_available = header(); + prev = header(); + next = header(); + } + + block_base_pointer header() BOOST_NOEXCEPT + { + return block_base_type::pointer_to(static_cast(*this)); + } + + const_block_base_pointer header() const BOOST_NOEXCEPT + { + return block_base_type::pointer_to(static_cast(*this)); + } + + BOOST_CONTAINER_FORCEINLINE void link_at_back(block_pointer pb) BOOST_NOEXCEPT + { + pb->link_before(header()); + } + + BOOST_CONTAINER_FORCEINLINE void link_before( + block_pointer pbx, block_pointer pby) BOOST_NOEXCEPT + { + pbx->link_before(pby); + } + + BOOST_CONTAINER_FORCEINLINE static void unlink(block_pointer pb) BOOST_NOEXCEPT + { + pb->unlink(); + } + + BOOST_CONTAINER_FORCEINLINE void link_available_at_back(block_pointer pb) BOOST_NOEXCEPT + { + pb->link_available_before(header()); + } + + BOOST_CONTAINER_FORCEINLINE void link_available_at_front(block_pointer pb) BOOST_NOEXCEPT + { + pb->link_available_after(header()); + } + + BOOST_CONTAINER_FORCEINLINE void unlink_available(block_pointer pb) BOOST_NOEXCEPT + { + pb->unlink_available(); + } + +private: + BOOST_MOVABLE_BUT_NOT_COPYABLE(block_list) +}; + +////////////////////////////////////////////// +// +// iterator +// +////////////////////////////////////////////// + +template +class iterator +{ + typedef typename boost::intrusive::pointer_traits::element_type element_type; + + +public: + typedef typename dtl::remove_const::type value_type; + typedef typename boost::intrusive::pointer_traits::difference_type difference_type; + typedef ValuePointer pointer; + typedef element_type& reference; + typedef std::bidirectional_iterator_tag iterator_category; + + struct nat + { + nat() : pbb(), n() {} + pointer pbb; + int n; + }; + + typedef typename nest_detail::pointer_rebind::type maybe_nonconst_pointer; + + typedef typename dtl::if_c< boost::move_detail::is_const::value + , iterator< maybe_nonconst_pointer > + , nat>::type maybe_nonconst_iterator; + + iterator() BOOST_NOEXCEPT + : pbb(), n(0) + {} + + iterator(const iterator& x) BOOST_NOEXCEPT + : pbb(x.pbb), n(x.n) + {} + + iterator(const maybe_nonconst_iterator& x) BOOST_NOEXCEPT + : pbb(x.pbb), n(x.n) + {} + + iterator& operator=(const iterator& x) BOOST_NOEXCEPT + { + pbb = x.pbb; + n = x.n; + return *this; + } + + iterator& operator=(const maybe_nonconst_iterator& x) BOOST_NOEXCEPT + { + pbb = x.pbb; + n = x.n; + return *this; + } + + pointer operator->() const BOOST_NOEXCEPT + { + return static_cast(*pbb).data() + n; + } + + reference operator*() const BOOST_NOEXCEPT + { + return *operator->(); + } + + BOOST_CONTAINER_FORCEINLINE iterator& operator++() BOOST_NOEXCEPT + { + mask_type m = pbb->mask & (full << 1 << std::size_t(n)); + if(BOOST_UNLIKELY(m == 0)) { + pbb = pbb->next; + BOOST_CONTAINER_NEST_PREFETCH_BLOCK(pbb->next, block_type); + m = pbb->mask; + } + n = nest_detail::unchecked_countr_zero(m); + return *this; + } + + BOOST_CONTAINER_FORCEINLINE iterator operator++(int) BOOST_NOEXCEPT + { + iterator tmp(*this); + this->operator++(); + return tmp; + } + + BOOST_CONTAINER_FORCEINLINE iterator& operator--() BOOST_NOEXCEPT + { + mask_type m = pbb->mask & (full >> 1 >> (N - 1 - std::size_t(n))); + if(BOOST_UNLIKELY(m == 0)) { + pbb = pbb->prev; + BOOST_CONTAINER_NEST_PREFETCH_BLOCK(pbb->prev, block_type); + m = pbb->mask; + } + n = int(N - 1 - (std::size_t)nest_detail::unchecked_countl_zero(m)); + return *this; + } + + BOOST_CONTAINER_FORCEINLINE iterator operator--(int) BOOST_NOEXCEPT + { + iterator tmp(*this); + this->operator--(); + return tmp; + } + + friend bool operator==(const iterator& x, const iterator& y) BOOST_NOEXCEPT + { + return x.pbb == y.pbb && x.n == y.n; + } + + friend bool operator!=(const iterator& x, const iterator& y) BOOST_NOEXCEPT + { + return !(x == y); + } + +private: + template friend class iterator; + template friend class container::nest; + + typedef typename pointer_rebind::type void_pointer; + typedef nest_detail::block_base block_base_type; + typedef typename pointer_rebind::type block_base_pointer; + typedef typename pointer_rebind::type const_block_base_pointer; + typedef typename pointer_rebind::type nonconst_pointer; + typedef nest_detail::block block_type; + typedef typename block_base_type::mask_type mask_type; + + BOOST_STATIC_CONSTEXPR std::size_t N = block_base_type::N; + BOOST_STATIC_CONSTEXPR mask_type full = block_base_type::full; + + iterator(const_block_base_pointer pbb_, int n_) BOOST_NOEXCEPT + : pbb(const_cast_block_base_pointer(pbb_)), n(n_) + {} + + explicit iterator(const_block_base_pointer pbb_) BOOST_NOEXCEPT + : pbb(const_cast_block_base_pointer(pbb_)) + , n(nest_detail::unchecked_countr_zero(pbb->mask)) + {} + + static block_base_pointer + const_cast_block_base_pointer(const_block_base_pointer pbb_) BOOST_NOEXCEPT + { + return block_base_type::pointer_to(const_cast(*pbb_)); + } + + block_base_pointer pbb; + int n; +}; + +////////////////////////////////////////////// +// +// sort_iterator +// +////////////////////////////////////////////// + +template +struct sort_iterator +{ + typedef T value_type; + typedef std::ptrdiff_t difference_type; + typedef T* pointer; + typedef T& reference; + typedef std::random_access_iterator_tag iterator_category; + + sort_iterator(T** pp_, std::size_t index_) + : pp(pp_), index(index_) + {} + + pointer operator->() const BOOST_NOEXCEPT + { return pp[index / N] + (index % N); } + + reference operator*() const BOOST_NOEXCEPT + { return *operator->(); } + + sort_iterator& operator++() BOOST_NOEXCEPT { ++index; return *this; } + sort_iterator operator++(int) BOOST_NOEXCEPT { sort_iterator t(*this); ++index; return t; } + sort_iterator& operator--() BOOST_NOEXCEPT { --index; return *this; } + sort_iterator operator--(int) BOOST_NOEXCEPT { sort_iterator t(*this); --index; return t; } + + friend difference_type + operator-(const sort_iterator& x, const sort_iterator& y) BOOST_NOEXCEPT + { return (difference_type)(x.index - y.index); } + + sort_iterator& operator+=(difference_type d) BOOST_NOEXCEPT + { index += std::size_t(d); return *this; } + + friend sort_iterator + operator+(const sort_iterator& x, difference_type d) BOOST_NOEXCEPT + { return sort_iterator(x.pp, x.index + static_cast(d)); } + + friend sort_iterator + operator+(difference_type d, const sort_iterator& x) BOOST_NOEXCEPT + { return sort_iterator(x.pp, d + x.index); } + + sort_iterator& operator-=(difference_type d) BOOST_NOEXCEPT + { index -= std::size_t(d); return *this; } + + friend sort_iterator + operator-(const sort_iterator& x, difference_type d) BOOST_NOEXCEPT + { return sort_iterator(x.pp, x.index - static_cast(d)); } + + reference operator[](difference_type d) const BOOST_NOEXCEPT + { return *(*this + d); } + + friend bool operator==(const sort_iterator& x, const sort_iterator& y) BOOST_NOEXCEPT + { return x.index == y.index; } + friend bool operator!=(const sort_iterator& x, const sort_iterator& y) BOOST_NOEXCEPT + { return x.index != y.index; } + friend bool operator< (const sort_iterator& x, const sort_iterator& y) BOOST_NOEXCEPT + { return x.index < y.index; } + friend bool operator> (const sort_iterator& x, const sort_iterator& y) BOOST_NOEXCEPT + { return x.index > y.index; } + friend bool operator<=(const sort_iterator& x, const sort_iterator& y) BOOST_NOEXCEPT + { return x.index <= y.index; } + friend bool operator>=(const sort_iterator& x, const sort_iterator& y) BOOST_NOEXCEPT + { return x.index >= y.index; } + + T** pp; + std::size_t index; +}; + +////////////////////////////////////////////// +// +// RAII helpers (replacing unique_ptr) +// +////////////////////////////////////////////// + +template +struct buffer +{ + typedef boost::container::allocator_traits alloc_traits; + + buffer(std::size_t n_, Allocator al_) + : al(al_), begin_idx(0), end_idx(0), cap(0), data(0) + { + data = static_cast(::operator new(n_ * sizeof(T), std::nothrow)); + if(data) cap = n_; + } + + ~buffer() + { + if(data) { + for(; begin_idx != end_idx; ++begin_idx) { + alloc_traits::destroy(al, data + begin_idx); + } + ::operator delete(static_cast(data)); + } + } + + T* begin() const BOOST_NOEXCEPT { return data + begin_idx; } + T* end() const BOOST_NOEXCEPT { return data + end_idx; } + + void push_back_move(T& v) + { + BOOST_ASSERT(data && end_idx != cap); + alloc_traits::construct(al, data + end_idx, boost::move(v)); + ++end_idx; + } + + void erase_front() BOOST_NOEXCEPT + { + BOOST_ASSERT(data && begin_idx != end_idx); + alloc_traits::destroy(al, data + begin_idx); + ++begin_idx; + } + + Allocator al; + std::size_t begin_idx; + std::size_t end_idx; + std::size_t cap; + T* data; + +private: + buffer(const buffer&); + buffer& operator=(const buffer&); +}; + +// RAII wrapper for raw memory (replaces unique_ptr with nodtor_deleter) +struct raw_memory_holder +{ + explicit raw_memory_holder(void* p_) BOOST_NOEXCEPT : p(p_) {} + ~raw_memory_holder() { if(p) ::operator delete(p); } + void release() BOOST_NOEXCEPT { p = 0; } + void* get() const BOOST_NOEXCEPT { return p; } + void* p; +private: + raw_memory_holder(const raw_memory_holder&); + raw_memory_holder& operator=(const raw_memory_holder&); +}; + +////////////////////////////////////////////// +// +// allocator propagation helpers +// +////////////////////////////////////////////// + +template +void copy_assign_if(dtl::true_type, T& x, const T& y) { x = y; } + +template +void copy_assign_if(dtl::false_type, T&, const T&) {} + +template +void move_assign_if(dtl::true_type, T& x, T& y) { x = boost::move(y); } + +template +void move_assign_if(dtl::false_type, T&, T&) {} + +////////////////////////////////////////////// +// +// block_typedefs +// +////////////////////////////////////////////// + +template +struct block_typedefs +{ + typedef boost::container::allocator_traits val_alloc_traits; + typedef typename val_alloc_traits::pointer value_pointer; + typedef typename pointer_rebind::type void_pointer; + + typedef nest_detail::block_base block_base_t; + typedef typename pointer_rebind< + value_pointer, block_base_t>::type block_base_pointer; + typedef typename pointer_rebind< + value_pointer, const block_base_t>::type const_block_base_pointer; + + typedef nest_detail::block block_t; + typedef typename pointer_rebind< + value_pointer, block_t>::type block_pointer; + + typedef typename val_alloc_traits:: + template portable_rebind_alloc::type block_allocator; + + typedef nest_detail::block_list block_list_t; +}; + +////////////////////////////////////////////// +// +// predicate adaptor for unique() +// +////////////////////////////////////////////// + +template +struct unique_pred_adaptor +{ + const T* value_ptr; + BinaryPredicate& pred; + unique_pred_adaptor(const T* v, BinaryPredicate& p) : value_ptr(v), pred(p) {} + bool operator()(const T& x) const { return pred(x, *value_ptr); } +}; + +////////////////////////////////////////////// +// +// sort_proxy_comparator +// +////////////////////////////////////////////// + +template +struct sort_proxy_comparator +{ + Compare& comp; + explicit sort_proxy_comparator(Compare& c) : comp(c) {} + + template + bool operator()(const SortProxy& x, const SortProxy& y) const + { + return comp( + const_cast(*x.p), + const_cast(*y.p)); + } +}; + +////////////////////////////////////////////// +// +// visit_to_visit_while adaptor +// +////////////////////////////////////////////// + +template +struct visit_adaptor +{ + F& f; + explicit visit_adaptor(F& f_) : f(f_) {} + bool operator()(T& x) const { f(x); return true; } +}; + +template +struct const_visit_adaptor +{ + F& f; + explicit const_visit_adaptor(F& f_) : f(f_) {} + bool operator()(const T& x) const { return f(x); } +}; + +} // namespace nest_detail + +#endif // BOOST_CONTAINER_DOXYGEN_INVOKED + +////////////////////////////////////////////////////////////////////////////// +// +// nest +// +////////////////////////////////////////////////////////////////////////////// + +template +class nest; + +//! A nest is a node-like container with a fixed block size of 64 elements +//! and bitmask-based slot management. It provides stable iterators and +//! efficient insertion and erasure operations in constant time. +//! +//! \tparam T The type of object stored in the nest +//! \tparam Allocator The allocator used for all internal memory management, use void +//! for the default allocator +#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED +template +#else +template +#endif +class nest + : private boost::empty_value< + typename nest_detail::block_typedefs< + typename real_allocator::type + >::block_allocator, 0> +{ + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + typedef typename real_allocator::type ValueAllocator; + typedef boost::container::allocator_traits allocator_traits_type; + typedef nest_detail::block_typedefs btd; + typedef typename btd::block_base_t block_base; + typedef typename btd::block_base_pointer block_base_pointer; + typedef typename btd::const_block_base_pointer const_block_base_pointer; + typedef typename btd::block_t block; + typedef typename btd::block_pointer block_pointer; + typedef typename btd::block_allocator block_allocator; + typedef typename btd::block_list_t block_list; + typedef boost::empty_value allocator_base; + typedef typename block_base::mask_type mask_type; + typedef boost::container::allocator_traits block_alloc_traits; + + BOOST_STATIC_CONSTEXPR std::size_t N = block_base::N; + BOOST_STATIC_CONSTEXPR mask_type full = block_base::full; + + BOOST_COPYABLE_AND_MOVABLE(nest) + #endif // BOOST_CONTAINER_DOXYGEN_INVOKED + + public: + ////////////////////////////////////////////// + // + // types + // + ////////////////////////////////////////////// + + typedef T value_type; + typedef ValueAllocator allocator_type; + typedef typename allocator_traits_type::pointer pointer; + typedef typename allocator_traits_type::const_pointer const_pointer; + typedef T& reference; + typedef const T& const_reference; + typedef typename allocator_traits_type::size_type size_type; + typedef typename allocator_traits_type::difference_type difference_type; + typedef BOOST_CONTAINER_IMPDEF(nest_detail::iterator) iterator; + typedef BOOST_CONTAINER_IMPDEF(nest_detail::iterator) const_iterator; + typedef BOOST_CONTAINER_IMPDEF(boost::container::reverse_iterator) reverse_iterator; + typedef BOOST_CONTAINER_IMPDEF(boost::container::reverse_iterator) const_reverse_iterator; + + ////////////////////////////////////////////// + // + // construct/copy/destroy + // + ////////////////////////////////////////////// + + //! Effects: Default constructs a nest. + //! + //! Throws: If allocator_type's default constructor throws. + //! + //! Complexity: Constant. + nest() BOOST_NOEXCEPT_IF(dtl::is_nothrow_default_constructible::value) + : allocator_base(boost::empty_init_t()) + , blist() + , num_blocks(0) + , size_(0) + {} + + //! Effects: Constructs a nest taking the allocator as parameter. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + explicit nest(const allocator_type& a) BOOST_NOEXCEPT_OR_NOTHROW + : allocator_base(boost::empty_init_t(), block_allocator(a)) + , blist() + , num_blocks(0) + , size_(0) + {} + + //! Effects: Constructs a nest and inserts n value-initialized elements. + //! + //! Throws: If allocator_type's default constructor + //! throws or T's default constructor throws. + //! + //! Complexity: Linear to n. + explicit nest(size_type n, const allocator_type& a = allocator_type()) + : allocator_base(boost::empty_init_t(), block_allocator(a)) + , blist() + , num_blocks(0) + , size_(0) + { + priv_insert_n_default(n); + } + + //! Effects: Constructs a nest and inserts n copies of x. + //! + //! Throws: If allocator_type's default constructor + //! throws or T's copy constructor throws. + //! + //! Complexity: Linear to n. + nest(size_type n, const T& x, const allocator_type& a = allocator_type()) + : allocator_base(boost::empty_init_t(), block_allocator(a)) + , blist() + , num_blocks(0) + , size_(0) + { + insert(n, x); + } + + //! Effects: Constructs a nest and inserts a copy of [first, last). + //! + //! Throws: If allocator_type's default constructor + //! throws or T's constructor taking a dereferenced InpIt throws. + //! + //! Complexity: Linear to the range [first, last). + template + nest(InpIt first, InpIt last + ,const allocator_type& a = allocator_type() + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename dtl::disable_if_convertible::type* = 0 + #endif + ) + : allocator_base(boost::empty_init_t(), block_allocator(a)) + , blist() + , num_blocks(0) + , size_(0) + { + insert(first, last); + } + + //! Effects: Copy constructs a nest. + //! + //! Postcondition: x == *this. + //! + //! Throws: If allocator_type's copy constructor throws. + //! + //! Complexity: Linear to the elements x contains. + nest(const nest& x) + : allocator_base(boost::empty_init_t(), block_allocator( + allocator_traits_type::select_on_container_copy_construction(x.priv_alloc()))) + , blist() + , num_blocks(0) + , size_(0) + { + insert(x.begin(), x.end()); + } + + //! Effects: Move constructor. Moves x's resources to *this. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + nest(BOOST_RV_REF(nest) x) BOOST_NOEXCEPT_OR_NOTHROW + : allocator_base(boost::empty_init_t(), boost::move(x.al())) + , blist(boost::move(x.blist)) + , num_blocks(x.num_blocks) + , size_(x.size_) + { + x.num_blocks = 0; + x.size_ = 0; + } + + //! Effects: Copy constructs a nest using the specified allocator. + //! + //! Postcondition: x == *this. + //! + //! Throws: If allocator_type's copy constructor throws. + //! + //! Complexity: Linear to the elements x contains. + nest(const nest& x, const allocator_type& a) + : allocator_base(boost::empty_init_t(), block_allocator(a)) + , blist() + , num_blocks(0) + , size_(0) + { + insert(x.begin(), x.end()); + } + + //! Effects: Move constructor using the specified allocator. + //! Moves x's resources to *this. + //! + //! Throws: If allocation or value_type's copy constructor throws. + //! + //! Complexity: Constant if a == x.get_allocator(), linear otherwise. + nest(BOOST_RV_REF(nest) x, const allocator_type& a) + : allocator_base(boost::empty_init_t(), block_allocator(a)) + , blist() + , num_blocks(0) + , size_(0) + { + if(al() == x.al()){ + blist = boost::move(x.blist); + num_blocks = x.num_blocks; + size_ = x.size_; + x.num_blocks = 0; + x.size_ = 0; + } + else{ + priv_insert_range_move(x.begin(), x.end()); + } + } + + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Constructs a nest and inserts elements from il. + //! + //! Complexity: Linear to the range [il.begin(), il.end()). + nest(std::initializer_list il, const allocator_type& a = allocator_type()) + : allocator_base(boost::empty_init_t(), block_allocator(a)) + , blist() + , num_blocks(0) + , size_(0) + { + insert(il.begin(), il.end()); + } + #endif + + //! Effects: Destroys the nest. All stored values are destroyed + //! and used memory is deallocated. + //! + //! Throws: Nothing. + //! + //! Complexity: Linear to the number of elements. + ~nest() BOOST_NOEXCEPT_OR_NOTHROW + { + priv_reset(); + } + + //! Effects: Makes *this contain the same elements as x. + //! + //! Throws: If memory allocation throws or T's copy constructor throws. + //! + //! Complexity: Linear to the number of elements in x. + nest& operator=(BOOST_COPY_ASSIGN_REF(nest) x) + { + if (BOOST_LIKELY(this != &x)) { + dtl::bool_ pocca; + if(priv_alloc() != x.priv_alloc() && pocca.value) { + priv_reset(); + nest_detail::copy_assign_if(pocca, al(), x.al()); + insert(x.begin(), x.end()); + } + else{ + nest_detail::copy_assign_if(pocca, al(), x.al()); + assign(x.begin(), x.end()); + } + } + return *this; + } + + //! Effects: Move assignment. All x's values are transferred to *this. + //! + //! Throws: If allocator_traits_type::propagate_on_container_move_assignment + //! is false and (allocation throws or value_type's move constructor throws). + //! + //! Complexity: Constant if allocator_traits_type:: + //! propagate_on_container_move_assignment is true or + //! this->get_allocator() == x.get_allocator(). Linear otherwise. + nest& operator=(BOOST_RV_REF(nest) x) + BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_move_assignment::value + || allocator_traits_type::is_always_equal::value) + { + if (BOOST_LIKELY(this != &x)) { + BOOST_STATIC_CONSTEXPR bool can_steal = + allocator_traits_type::propagate_on_container_move_assignment::value || + allocator_traits_type::is_always_equal::value; + priv_move_assign(x, dtl::bool_()); + } + return *this; + } + + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Assigns the elements of il to *this. + //! + //! Complexity: Linear. + nest& operator=(std::initializer_list il) + { + assign(il.begin(), il.end()); + return *this; + } + #endif + + //! Effects: Assigns [first, last) to *this. + //! + //! Complexity: Linear. + template + void assign(InpIt first, InpIt last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename dtl::disable_if_convertible::type* = 0 + #endif + ) + { + priv_range_assign(first, last); + } + + //! Effects: Assigns n copies of val to *this. + //! + //! Complexity: Linear. + void assign(size_type n, const T& val) + { + priv_assign_n(n, val); + } + + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Assigns the elements of il to *this. + //! + //! Complexity: Linear. + void assign(std::initializer_list il) + { assign(il.begin(), il.end()); } + #endif + + //! Effects: Returns a copy of the allocator. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + allocator_type get_allocator() const BOOST_NOEXCEPT_OR_NOTHROW + { return allocator_type(al()); } + + ////////////////////////////////////////////// + // + // iterators + // + ////////////////////////////////////////////// + + //! Effects: Returns an iterator to the first element. + //! + //! Complexity: Constant. + iterator begin() BOOST_NOEXCEPT { return ++end(); } + //! Effects: Returns a const_iterator to the first element. + const_iterator begin() const BOOST_NOEXCEPT { return ++end(); } + //! Effects: Returns an iterator to the end. + iterator end() BOOST_NOEXCEPT { return iterator(blist.header(), 0); } + //! Effects: Returns a const_iterator to the end. + const_iterator end() const BOOST_NOEXCEPT { return const_iterator(blist.header(), 0); } + //! Effects: Returns a reverse_iterator to the rbegin. + reverse_iterator rbegin() BOOST_NOEXCEPT { return reverse_iterator(end()); } + //! Effects: Returns a const_reverse_iterator to the rbegin. + const_reverse_iterator rbegin() const BOOST_NOEXCEPT { return const_reverse_iterator(end()); } + //! Effects: Returns a reverse_iterator to the rend. + reverse_iterator rend() BOOST_NOEXCEPT { return reverse_iterator(begin()); } + //! Effects: Returns a const_reverse_iterator to the rend. + const_reverse_iterator rend() const BOOST_NOEXCEPT { return const_reverse_iterator(begin()); } + //! Effects: Returns a const_iterator to the first element. + const_iterator cbegin() const BOOST_NOEXCEPT { return begin(); } + //! Effects: Returns a const_iterator to the end. + const_iterator cend() const BOOST_NOEXCEPT { return end(); } + //! Effects: Returns a const_reverse_iterator to the rbegin. + const_reverse_iterator crbegin()const BOOST_NOEXCEPT { return rbegin(); } + //! Effects: Returns a const_reverse_iterator to the rend. + const_reverse_iterator crend() const BOOST_NOEXCEPT { return rend(); } + + ////////////////////////////////////////////// + // + // capacity + // + ////////////////////////////////////////////// + + //! Effects: Returns true if the nest contains no elements. + //! + //! Complexity: Constant. + bool empty() const BOOST_NOEXCEPT { return size_ == 0; } + + //! Effects: Returns the number of elements. + //! + //! Complexity: Constant. + size_type size() const BOOST_NOEXCEPT { return size_; } + + //! Effects: Returns the maximum possible number of elements. + //! + //! Complexity: Constant. + size_type max_size() const BOOST_NOEXCEPT + { + std::size_t bs = (std::size_t)block_alloc_traits::max_size(al()) * sizeof(block); + allocator_type val_al(al()); + std::size_t vs = (std::size_t)allocator_traits_type::max_size(val_al) * sizeof(T); + return (size_type)((std::min)(bs, vs) / (sizeof(block) + sizeof(T) * N) * N); + } + + //! Effects: Returns the total number of slots (used and unused). + //! + //! Complexity: Constant. + size_type capacity() const BOOST_NOEXCEPT { return num_blocks * N; } + + //! Effects: Reserves space for at least n elements. + //! + //! Complexity: Linear. + void reserve(size_type n) + { + while(capacity() < n) (void)priv_create_new_available_block(); + } + + //! Effects: Compacts elements and removes unused blocks. + //! + //! Complexity: Linear. + void shrink_to_fit() + { + priv_compact_noop_track(); + trim_capacity(); + } + + //! Effects: Releases all reserved (empty) blocks. + //! + //! Complexity: Linear on the number of available blocks. + void trim_capacity() BOOST_NOEXCEPT { trim_capacity(0); } + + //! Effects: Releases reserved blocks until capacity() <= n. + //! + //! Complexity: Linear on the number of available blocks. + void trim_capacity(size_type n) BOOST_NOEXCEPT + { + block_base_pointer pbb = blist.header()->next_available; + while(capacity() > n && pbb != blist.header()) { + block_pointer pb = static_cast_block_pointer(pbb); + pbb = pbb->next_available; + if(pb->mask == 0) { + blist.unlink_available(pb); + priv_delete_block(pb); + --num_blocks; + } + } + } + + ////////////////////////////////////////////// + // + // modifiers + // + ////////////////////////////////////////////// + + #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) + + //! Effects: Inserts an element constructed in-place with args. + //! + //! Returns: An iterator to the inserted element. + //! + //! Complexity: Constant (amortized). + template + BOOST_CONTAINER_FORCEINLINE iterator emplace(BOOST_FWD_REF(Args)... args) + { + int n; + block_pointer pb = priv_retrieve_available_block(n); + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pb->data() + n), + boost::forward(args)...); + pb->mask |= pb->mask + 1; + if(BOOST_UNLIKELY(pb->mask + 1 <= 2)) { + if(pb->mask == 1) blist.link_at_back(pb); + else blist.unlink_available(pb); + } + ++size_; + return iterator(pb, n); + } + + //! Effects: Inserts an element constructed in-place with args (hint ignored). + //! + //! Returns: An iterator to the inserted element. + //! + //! Complexity: Constant (amortized). + template + BOOST_CONTAINER_FORCEINLINE iterator emplace_hint(const_iterator, BOOST_FWD_REF(Args)... args) + { return emplace(boost::forward(args)...); } + + #else // BOOST_NO_CXX11_VARIADIC_TEMPLATES + + #define BOOST_CONTAINER_NEST_EMPLACE_CODE(N) \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + BOOST_CONTAINER_FORCEINLINE iterator emplace(BOOST_MOVE_UREF##N) \ + { \ + int n_; \ + block_pointer pb = priv_retrieve_available_block(n_); \ + block_alloc_traits::construct( \ + al(), boost::movelib::to_raw_pointer(pb->data() + n_) \ + BOOST_MOVE_I##N BOOST_MOVE_FWD##N); \ + pb->mask |= pb->mask + 1; \ + if(BOOST_UNLIKELY(pb->mask + 1 <= 2)) { \ + if(pb->mask == 1) blist.link_at_back(pb); \ + else blist.unlink_available(pb); \ + } \ + ++size_; \ + return iterator(pb, n_); \ + } \ + \ + BOOST_MOVE_TMPL_LT##N BOOST_MOVE_CLASS##N BOOST_MOVE_GT##N \ + BOOST_CONTAINER_FORCEINLINE iterator emplace_hint(const_iterator BOOST_MOVE_I##N BOOST_MOVE_UREF##N) \ + { return emplace(BOOST_MOVE_FWD##N); } \ + // + BOOST_MOVE_ITERATE_0TO9(BOOST_CONTAINER_NEST_EMPLACE_CODE) + #undef BOOST_CONTAINER_NEST_EMPLACE_CODE + + #endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES + + //! Effects: Inserts a copy of x. + //! + //! Returns: An iterator to the inserted element. + //! + //! Complexity: Constant (amortized). + BOOST_CONTAINER_FORCEINLINE iterator insert(const T& x) + { return emplace(x); } + + //! Effects: Inserts a copy of x (hint ignored). + BOOST_CONTAINER_FORCEINLINE iterator insert(const_iterator, const T& x) + { return emplace(x); } + + //! Effects: Inserts x by moving. + //! + //! Returns: An iterator to the inserted element. + BOOST_CONTAINER_FORCEINLINE iterator insert(BOOST_RV_REF(value_type) x) + { return emplace(boost::move(x)); } + + //! Effects: Inserts x by moving (hint ignored). + BOOST_CONTAINER_FORCEINLINE iterator insert(const_iterator, BOOST_RV_REF(value_type) x) + { return emplace(boost::move(x)); } + + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + //! Effects: Inserts elements from il. + //! + //! Complexity: Linear. + void insert(std::initializer_list il) + { insert(il.begin(), il.end()); } + #endif + + //! Effects: Inserts copies of elements in [first, last). + //! + //! Complexity: Linear. + template + void insert(InpIt first, InpIt last + #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) + , typename dtl::disable_if_convertible::type* = 0 + #endif + ) + { + priv_insert_range_copy(first, last); + } + + //! Effects: Inserts n copies of x. + //! + //! Complexity: Linear. + void insert(size_type n, const T& x) + { + priv_insert_n_copies(n, x); + } + + //! Effects: Erases the element at position pos. + //! + //! Returns: An iterator to the element after the erased one. + //! + //! Complexity: Constant. + BOOST_CONTAINER_FORCEINLINE iterator erase(const_iterator pos) + { + block_base_pointer pbb = pos.pbb; + int n = pos.n; + ++pos; + priv_erase_impl(pbb, n); + return iterator(pos.pbb, pos.n); + } + + //! Effects: Erases the element at pos without returning iterator. + //! Potentially faster than erase(). + //! + //! Complexity: Constant. + BOOST_CONTAINER_FORCEINLINE void erase_void(const_iterator pos) + { + priv_erase_impl(pos.pbb, pos.n); + } + + //! Effects: Erases elements in [first, last). + //! + //! Returns: An iterator to the element after the erased range. + //! + //! Complexity: Linear to the range size. + iterator erase(const_iterator first, const_iterator last) + { + { + block_base_pointer pbb_first = first.pbb; + while(first != last) { + first = erase(first); + if(first.pbb != pbb_first) break; + } + } + block_base_pointer pbb = first.pbb; + if(pbb != last.pbb){ + do { + block_pointer pb = static_cast_block_pointer(pbb); + pbb = pb->next; + BOOST_CONTAINER_NEST_PREFETCH_BLOCK(pbb, block); + size_ -= priv_destroy_all_in_nonempty_block(pb); + blist.unlink(pb); + if(BOOST_UNLIKELY(pb->mask == full)) blist.link_available_at_front(pb); + pb->mask = 0; + } while(pbb != last.pbb); + first = const_iterator(pbb); + } + while(first != last) first = erase(first); + return iterator(last.pbb, last.n); + } + + //! Effects: Swaps the contents of *this and x. + //! + //! Throws: Nothing. + //! + //! Complexity: Constant. + void swap(nest& x) + BOOST_NOEXCEPT_IF(allocator_traits_type::propagate_on_container_swap::value + || allocator_traits_type::is_always_equal::value) + { + BOOST_STATIC_CONSTEXPR bool pocs_val = + allocator_traits_type::propagate_on_container_swap::value; + priv_swap_impl(x, dtl::bool_()); + } + + //! Effects: Erases all elements. + //! + //! Complexity: Linear. + void clear() BOOST_NOEXCEPT + { erase(begin(), end()); } + + ////////////////////////////////////////////// + // + // hive operations + // + ////////////////////////////////////////////// + + //! Effects: Transfers all elements from x into *this. + //! + //! Requires: get_allocator() == x.get_allocator(). + //! + //! Complexity: Linear in x.size(). + void splice(nest& x) + { + BOOST_ASSERT(this != &x); + BOOST_ASSERT(priv_alloc() == x.priv_alloc()); + block_base_pointer pbb = x.blist.header()->next; + while(pbb != x.blist.header()) { + block_pointer pb = static_cast_block_pointer(pbb); + pbb = pbb->next; + if(pb->mask != full) { + x.blist.unlink_available(pb); + blist.link_available_at_front(pb); + } + x.blist.unlink(pb); + blist.link_at_back(pb); + --x.num_blocks; + ++num_blocks; + size_type s = static_cast(boost::core::popcount(pb->mask)); + x.size_ -= s; + size_ += s; + } + } + + //! Effects: Transfers all elements from x into *this. + void splice(BOOST_RV_REF(nest) x) + { splice(x); } + + //! Effects: Removes consecutive duplicate elements. + //! + //! Returns: The number of elements removed. + //! + //! Complexity: Linear. + template + size_type unique(BinaryPredicate pred) + { + size_type s = size_; + const_iterator first = cbegin(); + const_iterator last = cend(); + while(first != last) { + const_iterator next_it = first; + ++next_it; + nest_detail::unique_pred_adaptor adaptor( + boost::addressof(*first), pred); + first = erase(next_it, + nest_detail::find_if_not(next_it, last, adaptor)); + } + return (size_type)(s - size_); + } + + //! Effects: Removes consecutive equal elements. + //! + //! Returns: The number of elements removed. + size_type unique() + { return unique(std::equal_to()); } + + //! Effects: Sorts elements according to comp. + //! + //! Complexity: O(n log n). + template + void sort(Compare comp) + { + priv_sort_impl(comp); + } + + //! Effects: Sorts elements in ascending order. + //! + //! Complexity: O(n log n). + void sort() + { sort(std::less()); } + + //! Effects: Returns an iterator to the element pointed to by p. + //! + //! Complexity: Linear. + iterator get_iterator(const_pointer p) BOOST_NOEXCEPT + { + std::less less_cmp; + block_base_pointer pbb = blist.next; + while(pbb != blist.header()) { + block_pointer pb = static_cast_block_pointer(pbb); + const T* raw_data = boost::movelib::to_raw_pointer(pb->data()); + const T* raw_p = boost::movelib::to_raw_pointer(p); + if(!less_cmp(raw_p, raw_data) && + less_cmp(raw_p, raw_data + N)) { + return iterator(pb, (int)(p - pb->data())); + } + pbb = pbb->next; + } + return end(); + } + + //! Effects: Returns a const_iterator to the element pointed to by p. + //! + //! Complexity: Linear. + const_iterator get_iterator(const_pointer p) const BOOST_NOEXCEPT + { + return const_cast(this)->get_iterator(p); + } + + ////////////////////////////////////////////// + // + // internal visitation + // + ////////////////////////////////////////////// + + //! Effects: Calls f(x) for each element x in [first, last). + //! + //! Complexity: Linear. + template + void visit(iterator first, iterator last, F f) + { + nest_detail::visit_adaptor adaptor(f); + visit_while(first, last, adaptor); + } + + //! Effects: Calls f(x) for each const element x in [first, last). + template + void visit(const_iterator first, const_iterator last, F f) const + { + nest_detail::visit_adaptor adaptor(f); + const_cast(this)->visit_while( + iterator(first.pbb, first.n), + iterator(last.pbb, last.n), + adaptor); + } + + //! Effects: Calls f(x) for each element x in [first, last) + //! until f returns false. + //! + //! Returns: Iterator to the element where visitation stopped. + template + iterator visit_while(iterator first, iterator last, F f) + { + { + block_base_pointer pbb = first.pbb; + while(first != last) { + if(!f(*first)) return first; + ++first; + if(first.pbb != pbb) break; + } + } + if(first.pbb != last.pbb) { + first = priv_visit_while_impl(first.pbb, last.pbb, f); + if(first.pbb != last.pbb) return first; + } + for(; first != last; ++first) if(!f(*first)) return first; + return first; + } + + //! Effects: Calls f(x) for each const element until f returns false. + //! + //! Returns: const_iterator to the element where visitation stopped. + template + const_iterator visit_while(const_iterator first, const_iterator last, F f) const + { + nest_detail::const_visit_adaptor adaptor(f); + iterator it = const_cast(this)->visit_while( + iterator(first.pbb, first.n), + iterator(last.pbb, last.n), + adaptor); + return const_iterator(it.pbb, it.n); + } + + //! Effects: Calls f(x) for all elements. + template + void visit_all(F f) + { visit(begin(), end(), f); } + + //! Effects: Calls f(x) for all const elements. + template + void visit_all(F f) const + { visit(begin(), end(), f); } + + //! Effects: Calls f(x) for all elements until f returns false. + //! + //! Returns: Iterator to the element where visitation stopped. + template + iterator visit_all_while(F f) + { return visit_while(begin(), end(), f); } + + //! Effects: Calls f(x) for all const elements until f returns false. + template + const_iterator visit_all_while(F f) const + { return visit_while(begin(), end(), f); } + + #ifndef BOOST_CONTAINER_DOXYGEN_INVOKED + private: + + template + friend typename nest::size_type erase_if(nest&, P); + + ////////////////////////////////////////////// + // + // private: allocator access + // + ////////////////////////////////////////////// + + block_allocator& al() BOOST_NOEXCEPT { return allocator_base::get(); } + const block_allocator& al() const BOOST_NOEXCEPT { return allocator_base::get(); } + + allocator_type priv_alloc() const BOOST_NOEXCEPT + { return allocator_type(al()); } + + ////////////////////////////////////////////// + // + // private: block management + // + ////////////////////////////////////////////// + + static block_pointer + static_cast_block_pointer(block_base_pointer pbb) BOOST_NOEXCEPT + { return block_list::static_cast_block_pointer(pbb); } + + block_pointer priv_create_new_available_block() + { + block_pointer pb = block_alloc_traits::allocate(al(), 1); + pb->mask = 0; + BOOST_TRY { + allocator_type val_al(al()); + pb->data_ = allocator_traits_type::allocate(val_al, N); + } + BOOST_CATCH(...) { + block_alloc_traits::deallocate(al(), pb, 1); + BOOST_RETHROW; + } + BOOST_CATCH_END + blist.link_available_at_back(pb); + ++num_blocks; + return pb; + } + + void priv_delete_block(block_pointer pb) BOOST_NOEXCEPT + { + allocator_type val_al(al()); + allocator_traits_type::deallocate(val_al, pb->data(), N); + block_alloc_traits::deallocate(al(), pb, 1); + } + + BOOST_CONTAINER_FORCEINLINE block_pointer priv_retrieve_available_block(int& n) + { + if(BOOST_LIKELY(blist.next_available != blist.header())){ + block_pointer pb = static_cast_block_pointer(blist.next_available); + n = nest_detail::unchecked_countr_one(pb->mask); + return pb; + } + else { + n = 0; + return priv_create_new_available_block(); + } + } + + ////////////////////////////////////////////// + // + // private: destruction helpers + // + ////////////////////////////////////////////// + + size_type priv_destroy_all_in_nonempty_block(block_pointer pb) BOOST_NOEXCEPT + { + BOOST_ASSERT(pb->mask != 0); + return priv_destroy_all_dispatch(pb, + dtl::bool_::value>()); + } + + size_type priv_destroy_all_dispatch( + block_pointer pb, dtl::true_type /* trivially destructible */) BOOST_NOEXCEPT + { + return (size_type)boost::core::popcount(pb->mask); + } + + size_type priv_destroy_all_dispatch( + block_pointer pb, dtl::false_type /* use destroy */) BOOST_NOEXCEPT + { + size_type s = 0; + mask_type m = pb->mask; + do { + int n = nest_detail::unchecked_countr_zero(m); + block_alloc_traits::destroy(al(), boost::movelib::to_raw_pointer(pb->data() + n)); + ++s; + m &= m - 1; + } while(m); + return s; + } + + size_type priv_destroy_all_in_full_block(block_pointer pb) BOOST_NOEXCEPT + { + BOOST_ASSERT(pb->mask == full); + for(std::size_t n = 0; n < N; ++n) { + block_alloc_traits::destroy(al(), boost::movelib::to_raw_pointer(pb->data() + n)); + } + return (size_type)N; + } + + ////////////////////////////////////////////// + // + // private: reset (destroy all + free) + // + ////////////////////////////////////////////// + + void priv_reset() BOOST_NOEXCEPT + { + // available blocks (with at least one empty slot) + block_base_pointer pbb = blist.header()->next_available; + while(pbb != blist.header()) { + block_pointer pb = static_cast_block_pointer(pbb); + pbb = pb->next_available; + if(pb->mask != 0) { + priv_destroy_all_in_nonempty_block(pb); + blist.unlink(pb); + } + priv_delete_block(pb); + } + // full blocks remaining + pbb = blist.next; + while(pbb != blist.header()) { + BOOST_ASSERT(pbb->mask == full); + block_pointer pb = static_cast_block_pointer(pbb); + pbb = pb->next; + priv_destroy_all_in_full_block(pb); + priv_delete_block(pb); + } + blist.reset(); + num_blocks = 0; + size_ = 0; + } + + ////////////////////////////////////////////// + // + // private: erase implementation + // + ////////////////////////////////////////////// + + BOOST_CONTAINER_FORCEINLINE void priv_erase_impl(block_base_pointer pbb, int n) BOOST_NOEXCEPT + { + block_pointer pb = static_cast_block_pointer(pbb); + block_alloc_traits::destroy(al(), boost::movelib::to_raw_pointer(pb->data() + n)); + if(BOOST_UNLIKELY(pb->mask == full)) blist.link_available_at_front(pb); + pb->mask &= ~((mask_type)(1) << n); + if(BOOST_UNLIKELY(pb->mask == 0)) blist.unlink(pb); + --size_; + } + + ////////////////////////////////////////////// + // + // private: range insert / assign + // + ////////////////////////////////////////////// + + template + void priv_insert_range_copy(InpIt first, InpIt last) + { + while(first != last) { + int n; + block_pointer pb = priv_retrieve_available_block(n); + for(; ; ) { + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pb->data() + n), *first); + ++first; + ++size_; + if(BOOST_UNLIKELY(pb->mask == 0)) blist.link_at_back(pb); + pb->mask |= pb->mask +1; + if(pb->mask == full){ + blist.unlink_available(pb); + break; + } + else if(first == last) return; + n = nest_detail::unchecked_countr_one(pb->mask); + } + } + } + + template + void priv_insert_range_move(InpIt first, InpIt last) + { + while(first != last) { + int n; + block_pointer pb = priv_retrieve_available_block(n); + for(; ; ) { + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pb->data() + n), boost::move(*first)); + ++first; + ++size_; + if(BOOST_UNLIKELY(pb->mask == 0)) blist.link_at_back(pb); + pb->mask |= pb->mask +1; + if(pb->mask == full){ + blist.unlink_available(pb); + break; + } + else if(first == last) return; + n = nest_detail::unchecked_countr_one(pb->mask); + } + } + } + + void priv_insert_n_copies(size_type count, const T& x) + { + for(size_type i = 0; i < count; ) { + int n; + block_pointer pb = priv_retrieve_available_block(n); + for(; ; ) { + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pb->data() + n), x); + ++i; ++size_; + if(BOOST_UNLIKELY(pb->mask == 0)) blist.link_at_back(pb); + pb->mask |= pb->mask +1; + if(pb->mask == full){ + blist.unlink_available(pb); + break; + } + else if(i == count) return; + n = nest_detail::unchecked_countr_one(pb->mask); + } + } + } + + void priv_insert_n_default(size_type count) + { + for(size_type i = 0; i < count; ) { + int n; + block_pointer pb = priv_retrieve_available_block(n); + for(; ; ) { + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pb->data() + n)); + ++i; ++size_; + if(BOOST_UNLIKELY(pb->mask == 0)) blist.link_at_back(pb); + pb->mask |= pb->mask +1; + if(pb->mask == full){ + blist.unlink_available(pb); + break; + } + else if(i == count) return; + n = nest_detail::unchecked_countr_one(pb->mask); + } + } + } + + template + void priv_range_assign(InpIt first, InpIt last) + { + block_base_pointer pbb = blist.next; + int n = 0; + if(first != last) { + for(; pbb != blist.header(); pbb = pbb->next, n = 0) { + block_pointer pb = static_cast_block_pointer(pbb); + for(mask_type bit = 1; bit; bit <<= 1, ++n) { + if(pb->mask & bit) { + pb->data()[n] = *first; + ++first; + } + else { + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pb->data() + n), *first); + ++first; + ++size_; + pb->mask |= bit; + if(pb->mask == full) blist.unlink_available(pb); + } + if(first == last) goto exit; + } + } + exit: ; + } + if(first != last) { + priv_insert_range_copy(first, last); + } + else { + const_iterator it = (n == 0) + ? const_iterator(pbb) + : (const_iterator(pbb, n), ++const_iterator(pbb, n)); + //Advance from pbb,n to the next valid position + if(n != 0) { + it = const_iterator(pbb, n); + ++it; + } else { + it = const_iterator(pbb); + } + erase(it, cend()); + } + } + + void priv_assign_n(size_type count, const T& val) + { + block_base_pointer pbb = blist.next; + int n = 0; + size_type remaining = count; + if(remaining > 0) { + for(; pbb != blist.header(); pbb = pbb->next, n = 0) { + block_pointer pb = static_cast_block_pointer(pbb); + for(mask_type bit = 1; bit; bit <<= 1, ++n) { + if(pb->mask & bit) { + pb->data()[n] = val; + --remaining; + } + else { + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pb->data() + n), val); + --remaining; + ++size_; + pb->mask |= bit; + if(pb->mask == full) blist.unlink_available(pb); + } + if(remaining == 0) goto exit; + } + } + exit: ; + } + if(remaining > 0) { + priv_insert_n_copies(remaining, val); + } + else { + const_iterator it; + if(n != 0) { + it = const_iterator(pbb, n); + ++it; + } else { + it = const_iterator(pbb); + } + erase(it, cend()); + } + } + + ////////////////////////////////////////////// + // + // private: move assign + // + ////////////////////////////////////////////// + + void priv_move_assign(nest& x, dtl::true_type /* can transfer */) + { + dtl::bool_ pocma; + priv_reset(); + nest_detail::move_assign_if(pocma, al(), x.al()); + blist = boost::move(x.blist); + num_blocks = x.num_blocks; + size_ = x.size_; + x.num_blocks = 0; + x.size_ = 0; + } + + void priv_move_assign(nest& x, dtl::false_type /* maybe move data */) + { + if(al() == x.al()) { + priv_move_assign(x, dtl::true_type()); + } + else { + // Move-assign element by element + priv_move_assign_elements(x); + } + } + + void priv_move_assign_elements(nest& x) + { + block_base_pointer pbb = blist.next; + int n = 0; + iterator first = x.begin(); + iterator last = x.end(); + if(first != last) { + for(; pbb != blist.header(); pbb = pbb->next, n = 0) { + block_pointer pb = static_cast_block_pointer(pbb); + for(mask_type bit = 1; bit; bit <<= 1, ++n) { + if(pb->mask & bit) { + pb->data()[n] = boost::move(*first); + ++first; + } + else { + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pb->data() + n), + boost::move(*first)); + ++first; + ++size_; + pb->mask |= bit; + if(pb->mask == full) blist.unlink_available(pb); + } + if(first == last) goto exit; + } + } + exit: ; + } + if(first != last) { + priv_insert_range_move(first, last); + } + else { + const_iterator it; + if(n != 0) { + it = const_iterator(pbb, n); + ++it; + } else { + it = const_iterator(pbb); + } + erase(it, cend()); + } + x.clear(); + } + + ////////////////////////////////////////////// + // + // private: swap + // + ////////////////////////////////////////////// + + void priv_swap_impl(nest& x, dtl::true_type /* propagate alloc */) + { + boost::adl_move_swap(al(), x.al()); + boost::adl_move_swap(blist, x.blist); + boost::adl_move_swap(num_blocks, x.num_blocks); + boost::adl_move_swap(size_, x.size_); + } + + void priv_swap_impl(nest& x, dtl::false_type /* don't propagate */) + { + BOOST_ASSERT(al() == x.al()); + boost::adl_move_swap(blist, x.blist); + boost::adl_move_swap(num_blocks, x.num_blocks); + boost::adl_move_swap(size_, x.size_); + } + + ////////////////////////////////////////////// + // + // private: sort + // + ////////////////////////////////////////////// + + struct sort_proxy + { + T* p; + size_type n; + }; + + template + void priv_sort_impl(Compare comp) + { + if(size_ <= 1) return; + + // Try transfer_sort for small element types + if(sizeof(T) <= sizeof(sort_proxy)) { + if(priv_transfer_sort(comp)) return; + } + else { + BOOST_STATIC_CONSTEXPR std::size_t memory_threshold = 2 * 1024 * 1024; + if((std::size_t)size_ * sizeof(sort_proxy) <= memory_threshold) { + if(priv_proxy_sort(comp)) return; + } + } + priv_compact_sort(comp); + } + + template + bool priv_transfer_sort(Compare comp) + { + nest_detail::buffer buf(size_, al()); + if(!buf.data) return false; + + // Move all elements to buffer + priv_visit_all_move_to_buffer(buf); + std::sort(buf.begin(), buf.end(), comp); + // Move sorted elements back + priv_visit_all_move_from_buffer(buf); + return true; + } + + void priv_visit_all_move_to_buffer(nest_detail::buffer& buf) + { + block_base_pointer pbb = blist.next; + while(pbb != blist.header()) { + block_pointer pb = static_cast_block_pointer(pbb); + pbb = pbb->next; + mask_type m = pb->mask; + while(m) { + int n = nest_detail::unchecked_countr_zero(m); + buf.push_back_move(pb->data()[n]); + m &= m - 1; + } + } + } + + void priv_visit_all_move_from_buffer(nest_detail::buffer& buf) + { + block_base_pointer pbb = blist.next; + while(pbb != blist.header()) { + block_pointer pb = static_cast_block_pointer(pbb); + pbb = pbb->next; + mask_type m = pb->mask; + while(m) { + int n = nest_detail::unchecked_countr_zero(m); + pb->data()[n] = boost::move(*buf.begin()); + buf.erase_front(); + m &= m - 1; + } + } + } + + template + bool priv_proxy_sort(Compare comp) + { + void* raw = ::operator new(size_ * sizeof(sort_proxy), std::nothrow); + if(!raw) return false; + nest_detail::raw_memory_holder holder(raw); + sort_proxy* proxies = static_cast(raw); + + size_type i = 0; + block_base_pointer pbb = blist.next; + while(pbb != blist.header()) { + block_pointer pb = static_cast_block_pointer(pbb); + pbb = pbb->next; + mask_type m = pb->mask; + while(m) { + int n = nest_detail::unchecked_countr_zero(m); + proxies[i].p = boost::movelib::to_raw_pointer(pb->data() + n); + proxies[i].n = i; + ++i; + m &= m - 1; + } + } + + nest_detail::sort_proxy_comparator proxy_comp(comp); + std::sort(proxies, proxies + size_, proxy_comp); + + // Rearrange elements according to sorted proxy order + for(i = 0; i < size_; ++i) { + if(proxies[i].n != i) { + T x = boost::move(*(proxies[i].p)); + size_type j = i; + do { + size_type k = proxies[j].n; + *(proxies[j].p) = boost::move(*proxies[k].p); + proxies[j].n = j; + j = k; + } while(proxies[j].n != i); + *(proxies[j].p) = boost::move(x); + proxies[j].n = j; + } + } + return true; + } + + template + void priv_compact_sort(Compare comp) + { + typedef nest_detail::sort_iterator sort_iter; + + std::size_t nblocks = (std::size_t)((size_ + N - 1) / N); + void* raw = ::operator new(nblocks * sizeof(T*)); + nest_detail::raw_memory_holder holder(raw); + T** ptrs = static_cast(raw); + + std::size_t idx = 0; + priv_compact_with_tracking(ptrs, idx); + BOOST_ASSERT(idx == nblocks); + + std::sort(sort_iter(ptrs, 0), sort_iter(ptrs, size_), comp); + } + + ////////////////////////////////////////////// + // + // private: compact + // + ////////////////////////////////////////////// + + void priv_compact_noop_track() + { + block_base_pointer pbbx = blist.next; + while(pbbx != blist.header()) { + block_pointer pbx = static_cast_block_pointer(pbbx); + block_base_pointer pbby = pbbx->next; + if(pbx->mask != full) { + do { + if(pbby->mask == full) { + do { + // skip full blocks (noop track) + pbby = pbby->next; + } while(pbby->mask == full); + blist.unlink(pbx); + blist.link_before(pbx, static_cast_block_pointer(pbby)); + } + if(pbby == blist.header()) { + priv_compact_single(pbx); + return; + } + else { + block_pointer pby = static_cast_block_pointer(pbby); + priv_compact_pair(pbx, pby); + if(pby->mask == 0) { + pbby = pby->next; + blist.unlink(pby); + } + } + } while(pbx->mask != full); + blist.unlink_available(pbx); + } + pbbx = pbby; + } + } + + void priv_compact_with_tracking(T** ptrs, std::size_t& idx) + { + block_base_pointer pbbx = blist.next; + while(pbbx != blist.header()) { + block_pointer pbx = static_cast_block_pointer(pbbx); + block_base_pointer pbby = pbbx->next; + if(pbx->mask != full) { + do { + if(pbby->mask == full) { + do { + ptrs[idx++] = boost::movelib::to_raw_pointer( + static_cast_block_pointer(pbby)->data()); + pbby = pbby->next; + } while(pbby->mask == full); + blist.unlink(pbx); + blist.link_before(pbx, static_cast_block_pointer(pbby)); + } + if(pbby == blist.header()) { + priv_compact_single(pbx); + ptrs[idx++] = boost::movelib::to_raw_pointer(pbx->data()); + return; + } + else { + block_pointer pby = static_cast_block_pointer(pbby); + priv_compact_pair(pbx, pby); + if(pby->mask == 0) { + pbby = pby->next; + blist.unlink(pby); + } + } + } while(pbx->mask != full); + blist.unlink_available(pbx); + } + ptrs[idx++] = boost::movelib::to_raw_pointer(pbx->data()); + pbbx = pbby; + } + } + + void priv_compact_pair(block_pointer& pbx, block_pointer& pby) + { + std::size_t cx = static_cast(boost::core::popcount(pbx->mask)); + std::size_t cy = static_cast(boost::core::popcount(pby->mask)); + if(cx < cy) { + boost::adl_move_swap(cx, cy); + nest_detail::swap_payload(*pbx, *pby); + } + std::size_t c = (std::min)(N - cx, cy); + while(c--) { + std::size_t n = static_cast(nest_detail::unchecked_countr_one(pbx->mask)); + std::size_t m = N - 1u - static_cast(nest_detail::unchecked_countl_zero(pby->mask)); + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pbx->data() + n), + boost::move(pby->data()[m])); + block_alloc_traits::destroy( + al(), boost::movelib::to_raw_pointer(pby->data() + m)); + pbx->mask |= pbx->mask + 1; + pby->mask &= ~((mask_type)(1) << m); + } + } + + void priv_compact_single(block_pointer pb) + { + for(; ;) { + std::size_t n = (std::size_t)nest_detail::unchecked_countr_one(pb->mask); + std::size_t m = N - 1 - (std::size_t)nest_detail::unchecked_countl_zero(pb->mask); + if(n > m) return; + block_alloc_traits::construct( + al(), boost::movelib::to_raw_pointer(pb->data() + n), + boost::move(pb->data()[m])); + block_alloc_traits::destroy( + al(), boost::movelib::to_raw_pointer(pb->data() + m)); + pb->mask |= pb->mask + 1; + pb->mask &= ~((mask_type)(1) << m); + } + } + + ////////////////////////////////////////////// + // + // private: visit_while implementation + // + ////////////////////////////////////////////// + + template + iterator priv_visit_while_impl( + block_base_pointer pbb, block_base_pointer last_pbb, F& f) + { + BOOST_ASSERT(pbb != last_pbb); + block_pointer pb = static_cast_block_pointer(pbb); + mask_type m = pb->mask; + int n = nest_detail::unchecked_countr_zero(m); + pointer pd = pb->data(); + do { + pbb = pb->next; + mask_type next_mask = pbb->mask; + int next_n = nest_detail::unchecked_countr_zero(next_mask); + pointer next_pd = static_cast_block_pointer(pbb)->data(); + BOOST_CONTAINER_NEST_PREFETCH(next_pd + next_n); + BOOST_CONTAINER_NEST_PREFETCH(pbb->next); + for(; ; ) { + if(!f(pd[n])) return iterator(pb, n); + m &= m - 1; + if(!m) break; + n = nest_detail::unchecked_countr_zero(m); + } + pb = static_cast_block_pointer(pbb); + m = next_mask; + n = next_n; + pd = next_pd; + } while(pb != last_pbb); + return iterator(last_pbb); + } + + ////////////////////////////////////////////// + // + // private: erase_if impl + // + ////////////////////////////////////////////// + + template + size_type priv_erase_if(Predicate pred) + { + size_type s = size_; + block_base_pointer pbb = blist.next; + while(pbb != blist.header()) { + block_pointer pb = static_cast_block_pointer(pbb); + pbb = pb->next; + BOOST_CONTAINER_NEST_PREFETCH_BLOCK(pbb, block); + mask_type m = pb->mask; + do { + int n = nest_detail::unchecked_countr_zero(m); + if(pred(pb->data()[n])) priv_erase_impl(pb, n); + m &= m - 1; + } while(m); + } + return (size_type)(s - size_); + } + + ////////////////////////////////////////////// + // + // private: data members + // + ////////////////////////////////////////////// + + block_list blist; + size_type num_blocks; + size_type size_; + + #endif // BOOST_CONTAINER_DOXYGEN_INVOKED +}; + +////////////////////////////////////////////// +// +// free functions +// +////////////////////////////////////////////// + +//! Effects: Swaps x and y. +template +inline void swap(nest& x, nest& y) + BOOST_NOEXCEPT_IF(BOOST_NOEXCEPT(x.swap(y))) +{ + x.swap(y); +} + +//! Effects: Erases all elements for which pred returns true. +//! +//! Returns: The number of erased elements. +template +typename nest::size_type +erase_if(nest& x, Predicate pred) +{ + return x.priv_erase_if(pred); +} + +//! Effects: Erases all elements equal to value. +//! +//! Returns: The number of erased elements. +template +typename nest::size_type +erase(nest& x, const T& value) +{ + return erase_if(x, equal_to_value(value)); +} + +#ifndef BOOST_CONTAINER_NO_CXX17_CTAD + +template< + class InpIt, + class Allocator = void +> +nest(InpIt, InpIt, Allocator = Allocator()) + -> nest< + typename iterator_traits::value_type, + Allocator>; + +#endif + +}} // namespace boost::container + +#if defined(BOOST_MSVC) +#pragma warning(pop) /* C4714 */ +#endif + +#include + +#endif // BOOST_CONTAINER_EXPERIMENTAL_NEST_HPP diff --git a/include/boost/container/experimental/pmr/nest.hpp b/include/boost/container/experimental/pmr/nest.hpp new file mode 100644 index 0000000..694cdd3 --- /dev/null +++ b/include/boost/container/experimental/pmr/nest.hpp @@ -0,0 +1,45 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2025-2026. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#ifndef BOOST_CONTAINER_EXPERIMENTAL_PMR_NEST_HPP +#define BOOST_CONTAINER_EXPERIMENTAL_PMR_NEST_HPP + +#if defined (_MSC_VER) +# pragma once +#endif + +#include +#include + +namespace boost { +namespace container { +namespace pmr { + +#if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) + +template +using nest = boost::container::nest>; + +#endif + +//! A portable metafunction to obtain a nest +//! that uses a polymorphic allocator +template +struct nest_of +{ + typedef boost::container::nest + < T, polymorphic_allocator > type; +}; + +} //namespace pmr { +} //namespace container { +} //namespace boost { + +#endif //BOOST_CONTAINER_EXPERIMENTAL_PMR_NEST_HPP diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6f52bb3..57bc40c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -58,6 +58,7 @@ boost_container_add_test(flat_set_adaptor_test flat_set_adaptor_test.cpp) boost_container_add_test(flat_set_test flat_set_test.cpp) boost_container_add_test(flat_tree_test flat_tree_test.cpp) boost_container_add_test(global_resource_test global_resource_test.cpp) +boost_container_add_test(nest_test nest_test.cpp) boost_container_add_test(insert_vs_emplace_test insert_vs_emplace_test.cpp) boost_container_add_test(list_test list_test.cpp) boost_container_add_test(map_test map_test.cpp) @@ -70,6 +71,7 @@ boost_container_add_test(pmr_deque_test pmr_deque_test.cpp) boost_container_add_test(pmr_devector_test pmr_devector_test.cpp) boost_container_add_test(pmr_flat_map_test pmr_flat_map_test.cpp) boost_container_add_test(pmr_flat_set_test pmr_flat_set_test.cpp) +boost_container_add_test(pmr_nest_test pmr_nest_test.cpp) boost_container_add_test(pmr_list_test pmr_list_test.cpp) boost_container_add_test(pmr_map_test pmr_map_test.cpp) boost_container_add_test(pmr_set_test pmr_set_test.cpp) diff --git a/test/nest_test.cpp b/test/nest_test.cpp new file mode 100644 index 0000000..3921d6d --- /dev/null +++ b/test/nest_test.cpp @@ -0,0 +1,337 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2025-2026. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include + +using namespace boost::container; + +void test_default_construction() +{ + nest h; + BOOST_TEST(h.empty()); + BOOST_TEST_EQ(h.size(), 0u); + BOOST_TEST(h.begin() == h.end()); +} + +void test_count_construction() +{ + nest h(5); + BOOST_TEST_EQ(h.size(), 5u); + // value-initialized ints should be 0 + for(nest::const_iterator it = h.begin(); it != h.end(); ++it) { + BOOST_TEST_EQ(*it, 0); + } +} + +void test_count_value_construction() +{ + nest h(3, 42); + BOOST_TEST_EQ(h.size(), 3u); + for(nest::const_iterator it = h.begin(); it != h.end(); ++it) { + BOOST_TEST_EQ(*it, 42); + } +} + +void test_range_construction() +{ + int arr[] = {1, 2, 3, 4, 5}; + nest h(arr, arr + 5); + BOOST_TEST_EQ(h.size(), 5u); +} + +void test_copy_construction() +{ + nest h1(3, 7); + nest h2(h1); + BOOST_TEST_EQ(h2.size(), 3u); + for(nest::const_iterator it = h2.begin(); it != h2.end(); ++it) { + BOOST_TEST_EQ(*it, 7); + } +} + +void test_move_construction() +{ + nest h1(3, 7); + nest h2(boost::move(h1)); + BOOST_TEST_EQ(h2.size(), 3u); + BOOST_TEST(h1.empty()); +} + +void test_insert_erase() +{ + nest h; + nest::iterator it1 = h.insert(10); + nest::iterator it2 = h.insert(20); + nest::iterator it3 = h.insert(30); + BOOST_TEST_EQ(h.size(), 3u); + + h.erase(it2); + BOOST_TEST_EQ(h.size(), 2u); + + h.erase(it1); + BOOST_TEST_EQ(h.size(), 1u); + + h.erase(it3); + BOOST_TEST(h.empty()); +} + +void test_emplace() +{ + nest h; + nest::iterator it = h.emplace(42); + BOOST_TEST_EQ(*it, 42); + BOOST_TEST_EQ(h.size(), 1u); +} + +void test_assign() +{ + nest h(5, 1); + h.assign(3u, 42); + BOOST_TEST_EQ(h.size(), 3u); + for(nest::const_iterator it = h.begin(); it != h.end(); ++it) { + BOOST_TEST_EQ(*it, 42); + } +} + +void test_copy_assignment() +{ + nest h1(3, 7); + nest h2; + h2 = h1; + BOOST_TEST_EQ(h2.size(), 3u); +} + +void test_move_assignment() +{ + nest h1(3, 7); + nest h2; + h2 = boost::move(h1); + BOOST_TEST_EQ(h2.size(), 3u); + BOOST_TEST(h1.empty()); +} + +void test_swap() +{ + nest h1(3, 1); + nest h2(5, 2); + h1.swap(h2); + BOOST_TEST_EQ(h1.size(), 5u); + BOOST_TEST_EQ(h2.size(), 3u); +} + +void test_clear() +{ + nest h(10, 5); + BOOST_TEST_EQ(h.size(), 10u); + h.clear(); + BOOST_TEST(h.empty()); +} + +void test_iterators() +{ + nest h; + h.insert(1); + h.insert(2); + h.insert(3); + + int count = 0; + for(nest::iterator it = h.begin(); it != h.end(); ++it) { + ++count; + } + BOOST_TEST_EQ(count, 3); + + count = 0; + for(nest::const_iterator it = h.cbegin(); it != h.cend(); ++it) { + ++count; + } + BOOST_TEST_EQ(count, 3); +} + +void test_reverse_iterators() +{ + nest h; + h.insert(1); + h.insert(2); + h.insert(3); + + int count = 0; + for(nest::reverse_iterator it = h.rbegin(); it != h.rend(); ++it) { + ++count; + } + BOOST_TEST_EQ(count, 3); +} + +void test_capacity() +{ + nest h; + BOOST_TEST_EQ(h.capacity(), 0u); + h.reserve(100); + BOOST_TEST(h.capacity() >= 100u); + h.trim_capacity(); + BOOST_TEST_EQ(h.capacity(), 0u); +} + +void test_sort() +{ + nest h; + h.insert(30); + h.insert(10); + h.insert(20); + h.sort(); + nest::const_iterator it = h.begin(); + BOOST_TEST_EQ(*it, 10); ++it; + BOOST_TEST_EQ(*it, 20); ++it; + BOOST_TEST_EQ(*it, 30); +} + +void test_unique() +{ + nest h; + h.insert(1); + h.insert(1); + h.insert(2); + h.insert(2); + h.insert(3); + h.sort(); + nest::size_type removed = h.unique(); + BOOST_TEST_EQ(removed, 2u); + BOOST_TEST_EQ(h.size(), 3u); +} + +struct less_3 + : public std::less +{ + less_3(int val) + : std::less() + , val_(val) + {} + + template + bool operator() (const T & val) const + { + return std::less::operator()(val, val_); + } + + int val_; +}; + +void test_erase_if() +{ + nest h; + h.insert(1); + h.insert(2); + h.insert(3); + h.insert(4); + h.insert(5); + nest::size_type removed = erase_if(h,less_3(3)); + BOOST_TEST_EQ(removed, 2u); + BOOST_TEST_EQ(h.size(), 3u); +} + +void test_erase_value() +{ + nest h; + h.insert(1); + h.insert(2); + h.insert(1); + h.insert(3); + nest::size_type removed = erase(h, 1); + BOOST_TEST_EQ(removed, 2u); + BOOST_TEST_EQ(h.size(), 2u); +} + +void test_splice() +{ + nest h1(3, 1); + nest h2(2, 2); + h1.splice(h2); + BOOST_TEST_EQ(h1.size(), 5u); + BOOST_TEST(h2.empty()); +} + +void test_large_insert_erase() +{ + nest h; + // Insert many elements + for(int i = 0; i < 1000; ++i) { + h.insert(i); + } + BOOST_TEST_EQ(h.size(), 1000u); + + // Erase all elements one by one + while(!h.empty()) { + h.erase(h.begin()); + } + BOOST_TEST(h.empty()); +} + +void test_shrink_to_fit() +{ + nest h; + for(int i = 0; i < 100; ++i) { + h.insert(i); + } + // Erase half + int count = 0; + nest::iterator it = h.begin(); + while(it != h.end()) { + if(count % 2 == 0) { + it = h.erase(it); + } else { + ++it; + } + ++count; + } + nest::size_type cap_before = h.capacity(); + h.shrink_to_fit(); + BOOST_TEST(h.capacity() <= cap_before); +} + +#if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) +void test_initializer_list() +{ + nest h = {1, 2, 3, 4, 5}; + BOOST_TEST_EQ(h.size(), 5u); +} +#endif + +int main() +{ + test_default_construction(); + test_count_construction(); + test_count_value_construction(); + test_range_construction(); + test_copy_construction(); + test_move_construction(); + test_insert_erase(); + test_emplace(); + test_assign(); + test_copy_assignment(); + test_move_assignment(); + test_swap(); + test_clear(); + test_iterators(); + test_reverse_iterators(); + test_capacity(); + test_sort(); + test_unique(); + test_erase_if(); + test_erase_value(); + test_splice(); + test_large_insert_erase(); + test_shrink_to_fit(); + #if !defined(BOOST_NO_CXX11_HDR_INITIALIZER_LIST) + test_initializer_list(); + #endif + return boost::report_errors(); +} diff --git a/test/pmr_nest_test.cpp b/test/pmr_nest_test.cpp new file mode 100644 index 0000000..b872ef4 --- /dev/null +++ b/test/pmr_nest_test.cpp @@ -0,0 +1,28 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2025-2026. Distributed under the Boost +// Software License, Version 1.0. (See accompanying file +// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/libs/container for documentation. +// +////////////////////////////////////////////////////////////////////////////// + +#include +#include + +int main() +{ + using namespace boost::container; + using boost::container::dtl::is_same; + + typedef nest > intcontainer_t; + BOOST_CONTAINER_STATIC_ASSERT(( is_same::type >::value )); + #if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) + BOOST_CONTAINER_STATIC_ASSERT(( is_same >::value )); + #endif + intcontainer_t cont(pmr::get_default_resource()); + typedef intcontainer_t::value_type value_type; + cont.insert(value_type()); + return 0; +}