From 4bed49ee140dca38b04739648cba1b8da2a4d9d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Sat, 1 Oct 2022 23:39:28 +0200 Subject: [PATCH] Reworked devector's relocation options, instead of relocation_limit, it's specified by single "relocate_on_XX" --- doc/container.qbk | 138 +++++++++++++----- example/doc_custom_devector.cpp | 103 ++++++++++++++ include/boost/container/devector.hpp | 35 +---- include/boost/container/options.hpp | 84 +++++++---- proj/vs/container.sln | 10 ++ proj/vs/copy_move_algo_test.vcxproj | 205 +++++++++++++++++++++++++++ proj/vs/doc_custom_devector.vcxproj | 187 ++++++++++++++++++++++++ test/copy_move_algo_test.cpp | 140 ++++++++++++++++++ test/devector_options_test.cpp | 20 +-- 9 files changed, 819 insertions(+), 103 deletions(-) create mode 100644 example/doc_custom_devector.cpp create mode 100644 proj/vs/copy_move_algo_test.vcxproj create mode 100644 proj/vs/doc_custom_devector.vcxproj create mode 100644 test/copy_move_algo_test.cpp diff --git a/doc/container.qbk b/doc/container.qbk index 7a27662..8fcdfac 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -471,7 +471,49 @@ implementation of methods that modify the devector at the front. In general, `de are a superset of those of `vector` with similar behaviour, barring a couple of iterator invalidation guarantees that differ. -The overhead for devector is one extra `size_t` per container: Usually sizeof(devector) == 4*sizeof(T*). +The static size overhead for boost's devector is one extra `size_t` per container: Usually sizeof(devector) == 4*sizeof(T*). + +There are different strategies when elements are to be inserted at one extreme of the container and there is +no room for additional elements at that extreme. One simple strategy would be to reallocate a bigger buffer +and move all elements to the new memory. However, this would lead to unbounded memory waste when elements are +inserted predominantly on one extreme (e.g. pushed at one extreme and popped from the other, like a LIFO pattern). + +To avoid unbounded memory waste, Boost.Container's `devector` uses a different strategy: + +* If elements are inserted near a extreme and there is free space on that extreme, the insertion is performed + without any additional data movement (only the elements between the insertion point and the extreme are moved. + +* If elements are inserted near one extreme and the free space on that extreme is exhausted, all existing elements + are relocated (moved) to the center of the internal memory buffer. This makes room in the exhausted extreme + to insert more elements whihout allocating a new buffer. + +* Potentially, the maximum number of possible relocations (movements) reusing the memory buffer are Olog(N), + but that would lead non-amortized constant-time insertion at the extremes. In consequence, the number of + relocations must be limited ('relocation limit') and a reallocation (allocation of a new memory buffer) will be + performed if the load-factor of the container defined as (size()/length_of_buffer) surpasses the relocation limit (see + Lars Greger Nordland Hagen's [href http://larshagencpp.github.io/blog/2016/05/22/devector"Double-ended vector - is it useful?"] + article for more details. + +* This approach offers a reasonable balance between a reasonable memory overhead and performance. + +However, this strategy has also some downsides: + +* Insertions at the extremes have no strong exception guarantee as data has to be move inside the existing buffer. + +* Due to the memory relocation vs reallocation strategy explained above: + * `capacity()` can no longer tell the maximum number of elements that the container can hold and, at the same time, + the number of insertions to perform before a reallocation is performed. Depending on which extreme a insertion + takes place, a reallocation might occur or not (maybe there is free capacity at that extreme) + + * Instead of removing the `capacity()` member or renaming it to "minimum_capacity()", the definition has been changed + to tell the *minimum* number of elements that can be inserted without reallocating. This allows the typical pattern + where: + * If `reserve(n)` is called, `capacity() >= n` + * If `capacity() == n` it is guaranteed that if `size() <= n` no reallocation will occur. + + * However the usual container invariant where `size() <= capacity()` does not hold. `size()` can be bigger than + `capacity()` because elements can be always inserted at a extreme with free capacity without reallocation. + [endsect] @@ -685,7 +727,7 @@ used to customize these containers: [endsect] -[section:configurable_vectors Configurable vectors] +[section:configurable_vector Configurable vector] The configuration for [classref boost::container::vector vector] is passed as the last template parameter and defined using the utility class @@ -706,42 +748,14 @@ the last template parameter and defined using the utility class `size()` and `capacity()` inside vector, so that the size of an empty vector is minimized and cache performance might be improved. See [classref boost::container::stored_size stored_size] for more details. -* [classref boost::container::devector devector] supports an addicional [classref boost::container::relocation_limit relocation_limit] - options to control container's behaviour when back or front free capacity is exhausted. - See the following example to see how [classref boost::container::vector_options vector_options] can be -used to customize `vector` container: +used to customize `vector`: [import ../example/doc_custom_vector.cpp] [doc_custom_vector] [endsect] -[section:configurable_deques Configurable deques] - -The configuration for [classref boost::container::deque deque] is passed as -the last template parameter and defined using the utility class -[classref boost::container::deque_options deque_options]. The following parameters can be configured: - -Parameters that control the size of deque's 'block' (deque allocates contiguous chunks of elements, called 'blocks'). -Only one of these paratemers can be specified: - -* [classref boost::container::block_bytes block_bytes]: the number of bytes deque will allocate for store - elements contiguously: `deque::get_block_size()` will return aproximately `block_bytes/sizeof(value_type)`. - A value of zero means the default value. - -* [classref boost::container::block_size block_size]: the number of elements deque will allocate contiguously. - If this option is specified, `deque::get_block_size()` will return the specified `block_size`. - A value of zero means the default value. - -See the following example to see how [classref boost::container::deque_options deque_options] can be -used to customize `deque` container: - -[import ../example/doc_custom_deque.cpp] -[doc_custom_deque] - -[endsect] - [section:configurable_static_vectors Configurable static vector] The configuration for [classref boost::container::static_vector static_vector] is passed as @@ -757,7 +771,7 @@ the last template parameter and defined using the utility class does not throw or abort, undefined behavior is triggered. See the following example to see how [classref boost::container::static_vector_options static_vector_options] can be -used to customize `static_vector` container: +used to customize `static_vector`: [import ../example/doc_custom_static_vector.cpp] [doc_custom_static_vector] @@ -784,13 +798,71 @@ the last template parameter and defined using the utility class [classref boost::container::growth_factor_50 growth_factor_100]. See the following example to see how [classref boost::container::small_vector_options small_vector_options] can be -used to customize `small_vector` container: +used to customize `small_vector`: [import ../example/doc_custom_small_vector.cpp] [doc_custom_small_vector] [endsect] +[section:configurable_deques Configurable deques] + +The configuration for [classref boost::container::deque deque] is passed as +the last template parameter and defined using the utility class +[classref boost::container::deque_options deque_options]. The following parameters can be configured: + +Parameters that control the size of deque's 'block' (deque allocates contiguous chunks of elements, called 'blocks'). +Only one of these paratemers can be specified: + +* [classref boost::container::block_bytes block_bytes]: the number of bytes deque will allocate for store + elements contiguously: `deque::get_block_size()` will return aproximately `block_bytes/sizeof(value_type)`. + A value of zero means the default value. + +* [classref boost::container::block_size block_size]: the number of elements deque will allocate contiguously. + If this option is specified, `deque::get_block_size()` will return the specified `block_size`. + A value of zero means the default value. + +See the following example to see how [classref boost::container::deque_options deque_options] can be +used to customize `deque`: + +[import ../example/doc_custom_deque.cpp] +[doc_custom_deque] + +[endsect] + +[section:configurable_devector Configurable devector] + +The configuration for [classref boost::container::devector devector] is passed as +the last template parameter and defined using the utility class +[classref boost::container::devector_options devector_options]. The following parameters can be configured: + +* [classref boost::container::growth_factor growth_factor]: the growth policy of the devector. + The rate at which the capacity of a devector grows is implementation dependent and + implementations choose exponential growth in order to meet the amortized constant time requirement for push_back. + A higher growth factor will make it faster as it will require less data movement, but it will have a greater memory + impact (on average, more memory will be unused). A user can provide a custom implementation of the growth factor and some + predefined policies are available: [classref boost::container::growth_factor_50 growth_factor_50], + [classref boost::container::growth_factor_60 growth_factor_60] (usually the default) and + [classref boost::container::growth_factor_100 growth_factor_100]. + +* [classref boost::container::stored_size stored_size]: the type that will be used to store size-related + parameters inside of the devector. Sometimes, when the maximum capacity to be used is much less than the + theoretical maximum that a devector can hold, it's interesting to use smaller unsigned integer types to represent + `size()` and `capacity()` inside devector, so that the size of an empty devector is minimized and cache + performance might be improved. See [classref boost::container::stored_size stored_size] for more details. + +* [classref boost::container::relocate_on_66 relocate_on_XX]: load factor limit that will determine if + new memory should be allocated or elements should relocated inside existing memory, when the free space + is exhausted at one end of the container. + +See the following example to see how [classref boost::container::devector_options devector_options] can be +used to customize `devector`: + +[import ../example/doc_custom_devector.cpp] +[doc_custom_devector] + +[endsect] + [endsect] [section:extended_allocators Extended functionality: Extended allocators] diff --git a/example/doc_custom_devector.cpp b/example/doc_custom_devector.cpp new file mode 100644 index 0000000..c5c7974 --- /dev/null +++ b/example/doc_custom_devector.cpp @@ -0,0 +1,103 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2022-2022. 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 +//[doc_custom_devector +#include +#include +/*<-*/ +#include +/*->*/ + +//Make sure assertions are active +#ifdef NDEBUG +#undef NDEBUG +#endif +#include + +int main () +{ + using namespace boost::container; + + //////////////////////////////////////////////// + // 'stored_size' option + //////////////////////////////////////////////// + //Specify that a devector will use "unsigned char" as the type to store size/capacity + typedef devector_options< stored_size >::type size_option_t; + + //Size-optimized devector is smaller than the default one. + typedef devector, size_option_t > size_optimized_devector_t; + BOOST_STATIC_ASSERT(( sizeof(size_optimized_devector_t) < sizeof(devector) )); + + //Requesting capacity for more elements than representable by "unsigned char" is an error + bool exception_thrown = false; + /*<-*/ + #ifndef BOOST_NO_EXCEPTIONS + BOOST_TRY{ size_optimized_devector_t v(256); } BOOST_CATCH(...){ exception_thrown = true; } BOOST_CATCH_END + #else + exception_thrown = true; + #endif //BOOST_NO_EXCEPTIONS + /*->*/ + //=try { size_optimized_devector_t v(256); } + //=catch(...){ exception_thrown = true; } + assert(exception_thrown == true); + + //////////////////////////////////////////////// + // 'growth_factor' option + //////////////////////////////////////////////// + //Specify that a devector will increase its capacity 50% when reallocating + typedef devector_options< growth_factor >::type growth_50_option_t; + + //Fill the devector until full capacity is reached + devector, growth_50_option_t > growth_50_dv(5, 0); + std::size_t old_cap = growth_50_dv.capacity(); + growth_50_dv.resize(old_cap); + + //Now insert an additional item and check the new buffer is 50% bigger + growth_50_dv.push_back(1); + assert(growth_50_dv.capacity() == old_cap*3/2); + + //////////////////////////////////////////////// + // 'relocate_on' option + //////////////////////////////////////////////// + + //Specifies that a devector will not reallocate but relocate elements if the free space + //at one end is exhausted and the total load factor is below the 66% threshold. + typedef devector_options< relocate_on_66 >::type relocation_66_option_t; + + //Configure devector to have equal free space at both ends + devector, relocation_66_option_t > reloc_66_dv + (16u, 16u, reserve_only_tag_t()); + old_cap = reloc_66_dv.capacity(); + const std::size_t front_free_cap = reloc_66_dv.front_free_capacity(); + + //Fill vector at the back end + while (reloc_66_dv.back_free_capacity() > 0) + reloc_66_dv.push_back(0); + + //Front free capacity is intact + assert(reloc_66_dv.front_free_capacity() == front_free_cap); + + //Now insert new element, values should relocated to the middle as the + //load factor is near 50% + reloc_66_dv.push_back(0); + assert(reloc_66_dv.capacity() == old_cap); + assert(reloc_66_dv.front_free_capacity() < front_free_cap); + + //Fill the back end again + while (reloc_66_dv.back_free_capacity() > 0) + reloc_66_dv.push_back(1); + + //New insertion should reallocate as load factor is higher than 66% + reloc_66_dv.push_back(-1); + assert(reloc_66_dv.capacity() > old_cap); + + return 0; +} +//] diff --git a/include/boost/container/devector.hpp b/include/boost/container/devector.hpp index fb164cb..e71e38f 100644 --- a/include/boost/container/devector.hpp +++ b/include/boost/container/devector.hpp @@ -62,31 +62,6 @@ namespace container { #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) -struct relocation_limit_66 -{ - static const std::size_t free_fraction = 3u; -}; - -struct relocation_limit_75 -{ - static const std::size_t free_fraction = 4u; -}; - -struct relocation_limit_80 -{ - static const std::size_t free_fraction = 5u; -}; - -struct relocation_limit_86 -{ - static const std::size_t free_fraction = 7u; -}; - -struct relocation_limit_90 -{ - static const std::size_t free_fraction = 10u; -}; - struct growth_factor_60; template @@ -94,14 +69,14 @@ struct get_devector_opt { typedef devector_opt< typename default_if_void::type , typename default_if_void::type - , typename default_if_void::type + , default_if_zero::value > type; }; template struct get_devector_opt { - typedef devector_opt< growth_factor_60, AllocatorSizeType, relocation_limit_90> type; + typedef devector_opt< growth_factor_60, AllocatorSizeType, relocate_on_90::value> type; }; #endif //#if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) @@ -178,7 +153,7 @@ class devector typedef typename options_type::growth_factor_type growth_factor_type; typedef typename options_type::stored_size_type stored_size_type; static const std::size_t devector_min_free_fraction = - options_type::relocation_limit_type::free_fraction; + options_type::free_fraction; #endif // ifndef BOOST_CONTAINER_DOXYGEN_INVOKED @@ -984,9 +959,9 @@ class devector /** * **Returns**: The *minimum* number of elements that can be inserted into devector using * position-based insertions without requiring a reallocation. Note that, unlike in - * typical sequence containers like `vector`, `capacity()` can be smaller than `size()`. + * typical sequence containers like `vector`, `capacity()`, `capacity()` can be smaller than `size()`. * This can happen if a user inserts elements in a particular way (usually inserting at - * front up to fron_free_capacity() and at back up to back_free_capacity()). + * front up to front_free_capacity() and at back up to back_free_capacity()). * * **Complexity**: Constant. */ diff --git a/include/boost/container/options.hpp b/include/boost/container/options.hpp index aff6987..c58ff82 100644 --- a/include/boost/container/options.hpp +++ b/include/boost/container/options.hpp @@ -200,6 +200,20 @@ struct default_if_void typedef Default type; }; +template +struct default_if_zero +{ + static const std::size_t value = N; +}; + +template +struct default_if_zero<0u, DefaultN> +{ + static const std::size_t value = DefaultN; +}; + + + #endif #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) @@ -468,65 +482,75 @@ using static_vector_options_t = typename boost::container::static_vector_options // //////////////////////////////////////////////////////////////// -//!This option setter specifies the relocation strategy of the underlying devector. +//!Thse options specify the relocation strategy of devector. //! -//!\tparam RelocationLimit A predefined occupation limit, used in insertions, that will determine -//! if the currently used memory buffer will be reused relocating all elements to the middle. If -//! the new occupation ratio (size()/current_buffer_size) is lower or equal than the limit, relocation -//! is performed reusing the same buffer. If the ratio is higher, a new buffer is allocated to hold -//! elements. -//! //!Predefined relocation limits that can be passed as arguments to this option are: -//!\c boost::container::relocation_limit_66 -//!\c boost::container::relocation_limit_75 -//!\c boost::container::relocation_limit_80 -//!\c boost::container::relocation_limit_86 -//!\c boost::container::relocation_limit_90 +//!\c boost::container::relocate_on_66 +//!\c boost::container::relocate_on_75 +//!\c boost::container::relocate_on_80 +//!\c boost::container::relocate_on_85 +//!\c boost::container::relocate_on_90 //! //!If this option is not specified, a default will be used by the container. //! //!Note: Repeated insertions at only one end (only back insertions or only front insertions) usually will -//!lead to a single relocation when `relocation_limit_66` is used and two relocations when `relocation_limit_90` +//!lead to a single relocation when `relocate_on_66` is used and two relocations when `relocate_on_90` //!is used. -BOOST_INTRUSIVE_OPTION_TYPE(relocation_limit, RelocLimit, RelocLimit, relocation_limit_type) #if !defined(BOOST_CONTAINER_DOXYGEN_INVOKED) -template +BOOST_INTRUSIVE_OPTION_CONSTANT(relocate_on, std::size_t, Fraction, free_fraction) + +struct relocate_on_66 : public relocate_on<3U>{}; + +struct relocate_on_75 : public relocate_on<4U> {}; + +struct relocate_on_80 : public relocate_on<5U> {}; + +struct relocate_on_85 : public relocate_on<7U> {}; + +struct relocate_on_90 : public relocate_on<10U> {}; + +template struct devector_opt : vector_opt { - typedef RelocLimit relocation_limit_type; + static const std::size_t free_fraction = FreeFraction; }; -typedef devector_opt devector_null_opt; +typedef devector_opt devector_null_opt; #else -//!This relocation limit argument specifies that the container will relocate +//!This relocation condition option specifies that the container will never relocate +//!elements when there is no space at the side the insertion should +//!take place +struct relocate_never; + +//!This relocation condition option specifies that the container will relocate //!all elements when there is no space at the side the insertion should //!take place and memory usage is below 66% (2/3) -struct relocation_limit_66{}; +struct relocate_on_66; -//!This relocation limit argument specifies that the container will relocate +//!This relocation condition option specifies that the container will relocate //!all elements when there is no space at the side the insertion should //!take place and memory usage is below 75% (3/4) -struct relocation_limit_75 {}; +struct relocate_on_75; -//!This relocation limit argument specifies that the container will relocate +//!This relocation condition option specifies that the container will relocate //!all elements when there is no space at the side the insertion should //!take place and memory usage is below 80% (4/5) -struct relocation_limit_80 {}; +struct relocate_on_80; -//!This relocation limit argument specifies that the container will relocate +//!This relocation condition option specifies that the container will relocate //!all elements when there is no space at the side the insertion should -//!take place and memory usage is below 86% (6/7) -struct relocation_limit_86 {}; +//!take place and memory usage is below 85% (6/7) +struct relocate_on_85; -//!This relocation limit argument specifies that the container will relocate +//!This relocation condition option specifies that the container will relocate //!all elements when there is no space at the side the insertion should //!take place and memory usage is below 90% (9/10) -struct relocation_limit_90 {}; +struct relocate_on_90; #endif @@ -534,7 +558,7 @@ struct relocation_limit_90 {}; //! Helper metafunction to combine options into a single type to be used //! by \c boost::container::devector. //! Supported options are: \c boost::container::growth_factor, \c boost::container::stored_size -//! and \c boost::container::relocation_limit +//! and \c boost::container::relocate_on #if defined(BOOST_CONTAINER_DOXYGEN_INVOKED) || defined(BOOST_CONTAINER_VARIADIC_TEMPLATES) template #else @@ -553,7 +577,7 @@ struct devector_options >::type packed_options; typedef devector_opt< typename packed_options::growth_factor_type , typename packed_options::stored_size_type - , typename packed_options::relocation_limit_type + , packed_options::free_fraction > implementation_defined; /// @endcond typedef implementation_defined type; diff --git a/proj/vs/container.sln b/proj/vs/container.sln index d95d567..198ad8f 100644 --- a/proj/vs/container.sln +++ b/proj/vs/container.sln @@ -208,6 +208,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hash_map_test", "hash_map_t EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "copy_move_algo_test", "copy_move_algo_test.vcxproj", "{5CE11C83-84A7-093A-4FA2-A6BA20A30592}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "doc_custom_devector", "doc_custom_devector.vcxproj", "{DB7B1CED-5670-8478-4C9D-F3BCF4A4209A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -996,6 +998,14 @@ Global {5CE11C83-84A7-093A-4FA2-A6BA20A30592}.Release|x64.Build.0 = Release|x64 {5CE11C83-84A7-093A-4FA2-A6BA20A30592}.Release|x86.ActiveCfg = Release|Win32 {5CE11C83-84A7-093A-4FA2-A6BA20A30592}.Release|x86.Build.0 = Release|Win32 + {DB7B1CED-5670-8478-4C9D-F3BCF4A4209A}.Debug|x64.ActiveCfg = Debug|x64 + {DB7B1CED-5670-8478-4C9D-F3BCF4A4209A}.Debug|x64.Build.0 = Debug|x64 + {DB7B1CED-5670-8478-4C9D-F3BCF4A4209A}.Debug|x86.ActiveCfg = Debug|Win32 + {DB7B1CED-5670-8478-4C9D-F3BCF4A4209A}.Debug|x86.Build.0 = Debug|Win32 + {DB7B1CED-5670-8478-4C9D-F3BCF4A4209A}.Release|x64.ActiveCfg = Release|x64 + {DB7B1CED-5670-8478-4C9D-F3BCF4A4209A}.Release|x64.Build.0 = Release|x64 + {DB7B1CED-5670-8478-4C9D-F3BCF4A4209A}.Release|x86.ActiveCfg = Release|Win32 + {DB7B1CED-5670-8478-4C9D-F3BCF4A4209A}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/proj/vs/copy_move_algo_test.vcxproj b/proj/vs/copy_move_algo_test.vcxproj new file mode 100644 index 0000000..38cd8e4 --- /dev/null +++ b/proj/vs/copy_move_algo_test.vcxproj @@ -0,0 +1,205 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {5CE11C83-84A7-093A-4FA2-A6BA20A30592} + Win32Proj + 10.0 + + + + Application + v143 + MultiByte + + + Application + v143 + MultiByte + + + Application + v143 + MultiByte + + + Application + v143 + MultiByte + + + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>15.0.27625.0 + + + $(Platform)\$(Configuration)\$(TargetName)\ + $(Platform)\$(Configuration)\$(TargetName)\Int\ + false + $(ProjectName) + + + false + $(ProjectName) + $(Platform)\$(Configuration)\$(TargetName)\ + $(Platform)\$(Configuration)\$(TargetName)\Int\ + + + $(Platform)\$(Configuration)\$(TargetName)\ + $(Platform)\$(Configuration)\$(TargetName)\Int\ + false + $(ProjectName) + + + false + $(ProjectName) + $(Platform)\$(Configuration)\$(TargetName)\ + $(Platform)\$(Configuration)\$(TargetName)\Int\ + + + + Disabled + ../../../..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions);_ITERATOR_DEBUG_LEVEL=0 + Sync + EnableFastChecks + MultiThreadedDebugDLL + true + + Level4 + ProgramDatabase + stdcpplatest + /Zc:__cplusplus %(AdditionalOptions) + false + false + + + winmm.lib;%(AdditionalDependencies) + ../../../../stage/lib;%(AdditionalLibraryDirectories) + true + $(OutDir)copy_move_algo_test.pdb + Console + MachineX86 + false + + + + + Disabled + ../../../..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + Sync + EnableFastChecks + MultiThreadedDebugDLL + true + + + Level4 + ProgramDatabase + stdcpplatest + /Zc:__cplusplus %(AdditionalOptions) + false + false + + + winmm.lib;%(AdditionalDependencies) + ../../../../stage/lib;%(AdditionalLibraryDirectories) + true + $(OutDir)copy_move_algo_test.pdb + Console + false + + + + + ../../../..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level4 + + stdcpplatest + /Zc:__cplusplus %(AdditionalOptions) + false + false + Sync + + + winmm.lib;%(AdditionalDependencies) + ../../../../stage/lib;%(AdditionalLibraryDirectories) + true + Console + true + true + MachineX86 + false + + + + + ../../../..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level4 + + + stdcpplatest + /Zc:__cplusplus %(AdditionalOptions) + false + false + Sync + + + winmm.lib;%(AdditionalDependencies) + ../../../../stage/lib;%(AdditionalLibraryDirectories) + true + Console + true + true + false + + + + + + + + + \ No newline at end of file diff --git a/proj/vs/doc_custom_devector.vcxproj b/proj/vs/doc_custom_devector.vcxproj new file mode 100644 index 0000000..da4bd7e --- /dev/null +++ b/proj/vs/doc_custom_devector.vcxproj @@ -0,0 +1,187 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {DB7B1CED-5670-8478-4C9D-F3BCF4A4209A} + Win32Proj + 10.0 + + + + Application + v143 + MultiByte + + + Application + v143 + MultiByte + + + Application + v143 + MultiByte + + + Application + v143 + MultiByte + + + + + + + + + + + + + + + + + + + <_ProjectFileVersion>15.0.27625.0 + + + $(Platform)\$(Configuration)\$(TargetName)\ + $(Platform)\$(Configuration)\$(TargetName)\Int\ + false + $(ProjectName) + + + false + $(ProjectName) + $(Platform)\$(Configuration)\$(TargetName)\ + $(Platform)\$(Configuration)\$(TargetName)\Int\ + + + $(Platform)\$(Configuration)\$(TargetName)\ + $(Platform)\$(Configuration)\$(TargetName)\Int\ + false + + + false + $(Platform)\$(Configuration)\$(TargetName)\ + $(Platform)\$(Configuration)\$(TargetName)\Int\ + + + + Disabled + ../../../..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + Sync + EnableFastChecks + MultiThreadedDebugDLL + true + + Level4 + ProgramDatabase + Default + + + winmm.lib;%(AdditionalDependencies) + ../../../../stage/lib;%(AdditionalLibraryDirectories) + true + $(OutDir)doc_custom_devector.pdb + Console + MachineX86 + false + + + + + Disabled + ../../../..;%(AdditionalIncludeDirectories) + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + Sync + EnableFastChecks + MultiThreadedDebugDLL + true + + + Level4 + ProgramDatabase + Default + + + winmm.lib;%(AdditionalDependencies) + ../../../../stage/lib;%(AdditionalLibraryDirectories) + true + $(OutDir)doc_custom_devector.pdb + Console + false + + + + + ../../../..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + Level4 + + Default + + + winmm.lib;%(AdditionalDependencies) + ../../../../stage/lib;%(AdditionalLibraryDirectories) + true + Console + true + true + MachineX86 + false + + + + + ../../../..;%(AdditionalIncludeDirectories) + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + MultiThreadedDLL + true + + + Level4 + + + Default + + + winmm.lib;%(AdditionalDependencies) + ../../../../stage/lib;%(AdditionalLibraryDirectories) + true + Console + true + true + false + + + + + + + + + \ No newline at end of file diff --git a/test/copy_move_algo_test.cpp b/test/copy_move_algo_test.cpp new file mode 100644 index 0000000..78ac1dc --- /dev/null +++ b/test/copy_move_algo_test.cpp @@ -0,0 +1,140 @@ +////////////////////////////////////////////////////////////////////////////// +// +// (C) Copyright Ion Gaztanaga 2004-2013. 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. +// +////////////////////////////////////////////////////////////////////////////// + +// the tests trigger deprecation warnings when compiled with msvc in C++17 mode +#if defined(_MSVC_LANG) && _MSVC_LANG > 201402 +// warning STL4009: std::allocator is deprecated in C++17 +# define _SILENCE_CXX17_ALLOCATOR_VOID_DEPRECATION_WARNING +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "movable_int.hpp" + +using namespace boost::container; + +namespace boost { +namespace container { +namespace test { + +//This function tests all the possible combinations when +//inserting data in a vector and expanding backwards +template +void test_expand_backward_forward_and_insert_alloc() +{ + typedef ValueType value_type; + + //Distance old and new buffer + const unsigned int Offset[] = + { 350, 300, 250, 200, 150, 100, 150, 100, + 150, 50, 50, 50 }; + //Initial vector size + const unsigned int InitialSize[] = + { 200, 200, 200, 200, 200, 200, 200, 200, + 200, 200, 200, 200 }; + //Size of the data to insert + const unsigned int InsertSize[] = + { 100, 100, 100, 100, 100, 100, 200, 200, + 300, 25, 100, 200 }; + //Number of tests + const unsigned int Iterations = sizeof(InsertSize) / sizeof(int); + + //Insert position + const int Position[] = + { 0, 100, 200 }; + + for (unsigned backmove = 2u; backmove != 0u; ) { + --backmove; + for (unsigned int pos = 0; pos < sizeof(Position) / sizeof(Position[0]); ++pos) { + BOOST_TEST(life_count::check(0)); + + for (unsigned int iteration = 0; iteration < Iterations; ++iteration) + { + typedef std::vector std_vector_val_t; + typedef boost::container::new_allocator allocator_type; + typedef dtl::insert_range_proxy proxy_t; + { + + std_vector_val_t initial_data; + initial_data.resize(InitialSize[iteration]); + for (unsigned int i = 0; i < InitialSize[iteration]; ++i) { + initial_data[i] = static_cast((int)i); + } + BOOST_TEST(life_count::check(InitialSize[iteration])); + + std_vector_val_t data_to_insert; + data_to_insert.resize(InsertSize[iteration]); + for (unsigned int i = 0; i < InsertSize[iteration]; ++i) { + data_to_insert[i] = static_cast(-(int)i); + } + + BOOST_TEST(life_count::check(InitialSize[iteration] + InsertSize[iteration])); + + const unsigned int BufferSize = InitialSize[iteration] + InsertSize[iteration] + Offset[iteration]; + boost::movelib::unique_ptr memptr = + boost::movelib::make_unique_definit(BufferSize * sizeof(value_type)); + value_type* memory = move_detail::force_ptr(memptr.get()); + allocator_type a; + + value_type* final_memory; + value_type* initial_memory; + if(backmove){ + initial_memory = memory + Offset[iteration]; + final_memory = memory; + } + else{ + initial_memory = memory + BufferSize - InitialSize[iteration] - Offset[iteration]; + final_memory = memory + BufferSize - InitialSize[iteration] - InsertSize[iteration]; + } + + for (unsigned int i = 0; i < InitialSize[iteration]; ++i) { + allocator_traits::construct(a, &initial_memory[i], (int)i); + } + + proxy_t proxy(data_to_insert.begin()); + boost::container::expand_backward_forward_and_insert_alloc + (initial_memory, initial_data.size(), final_memory, initial_memory + Position[pos] + , data_to_insert.size(), proxy, a); + + BOOST_TEST(life_count::check(InitialSize[iteration] * 2 + InsertSize[iteration] * 2)); + + initial_data.insert(initial_data.begin() + Position[pos] + , data_to_insert.begin(), data_to_insert.end()); + + //Now check that values are equal + BOOST_TEST(boost::container::algo_equal(initial_data.begin(), initial_data.end(), final_memory)); + + boost::container::destroy_alloc_n(a, final_memory, InitialSize[iteration] + InsertSize[iteration]); + } + BOOST_TEST(life_count::check(0)); + } + } + } +} + +} //namespace test { +} //namespace container { +} //namespace boost { + +using namespace boost::container; + +int main() +{ + test::test_expand_backward_forward_and_insert_alloc(); + return boost::report_errors(); +} diff --git a/test/devector_options_test.cpp b/test/devector_options_test.cpp index 9e9dd0b..c9eba7f 100644 --- a/test/devector_options_test.cpp +++ b/test/devector_options_test.cpp @@ -111,13 +111,14 @@ void test_growth_factor_100() BOOST_TEST(new_capacity == 2*old_capacity); } -void test_stored_relloc_limit_66() + +void test_stored_reloc_on_66() { #if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) - using options_t = devector_options_t< relocation_limit >; + using options_t = devector_options_t< relocate_on_66 >; #else typedef devector_options - < relocation_limit >::type options_t; + < relocate_on_66 >::type options_t; #endif const std::size_t buffer_size = 32u; const std::size_t initial_side = buffer_size/2u; @@ -147,17 +148,16 @@ void test_stored_relloc_limit_66() //New insertion should reallocate v.push_back(-1); - BOOST_TEST(v.back_free_capacity() > 8); + BOOST_TEST(v.back_free_capacity() > initial_side/2u); BOOST_TEST(v.capacity() > old_cp); } -void test_stored_relloc_limit_90() +void test_stored_reloc_on_90() { #if !defined(BOOST_NO_CXX11_TEMPLATE_ALIASES) - using options_t = devector_options_t< relocation_limit >; + using options_t = devector_options_t< relocate_on_90 >; #else - typedef devector_options - < relocation_limit >::type options_t; + typedef devector_options< relocate_on_90 >::type options_t; #endif const std::size_t buffer_size = 32u; @@ -210,7 +210,7 @@ int main() test_growth_factor_100(); test_stored_size_type(); test_stored_size_type(); - test_stored_relloc_limit_66(); - test_stored_relloc_limit_90(); + test_stored_reloc_on_66(); + test_stored_reloc_on_90(); return ::boost::report_errors(); }