From 9e2afe85ad20b93bf8766396188da744b02a5a26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ion=20Gazta=C3=B1aga?= Date: Sat, 24 Jan 2026 23:29:36 +0100 Subject: [PATCH] Add "inline_chars" option to basic_string --- doc/container.qbk | 10 ++++++ example/doc_custom_string.cpp | 28 +++++++++++++++-- include/boost/container/options.hpp | 27 +++++++++++++++-- include/boost/container/string.hpp | 47 +++++++++++++++++++++-------- 4 files changed, 95 insertions(+), 17 deletions(-) diff --git a/doc/container.qbk b/doc/container.qbk index dee04d1..2c13e40 100644 --- a/doc/container.qbk +++ b/doc/container.qbk @@ -882,6 +882,12 @@ the last template parameter and defined using the utility class small string optimization buffer might be reduced in size if a smaller than `std::size_t` `stored_size` unsigned type is used. +* [classref boost::container::inline_chars inline_chars]: the number of characters (excluding terminating null) that + will be stored inside `basic_string` (small string optimization) before using dynamic allocation. Due to internal + data structure limitations, this value must be equal or less than 127. The implementation will guarantee at least + that number of characters in the internal buffer, but it might be more depending on alignment issues and the `stored_size` + option. + See the following example to see how [classref boost::container::string_options string_options] can be used to customize `string_options`: @@ -1472,6 +1478,10 @@ use [*Boost.Container]? There are several reasons for that: * Added C++20 `starts_with`/`ends_with` and C++23 `contains` overloads. * Added null pointer checks for `basic_string` methods taking `const CharT*` parameters. * Deleted constructor from `std::nullptr_t`. + * Added [classref boost::container::inline_chars inline_chars], + [classref boost::container::growth_factor growth_factor] and + [classref boost::container::stored_size stored_size] options to [classref boost::container::basic_string basic_string]. + * Fixed bugs/issues: * [@https://github.com/boostorg/container/issues/323 GitHub #323: ['"flat_tree::try_emplace UB"]]. * [@https://github.com/boostorg/container/issues/328 GitHub #328: ['"boost::container::deque stores a redundant copy of the allocator, increasing size"]]. diff --git a/example/doc_custom_string.cpp b/example/doc_custom_string.cpp index 152fa77..37ec43c 100644 --- a/example/doc_custom_string.cpp +++ b/example/doc_custom_string.cpp @@ -20,6 +20,10 @@ int main () { using namespace boost::container; +//-------------------------- +// Option "stored_size" +//-------------------------- + //This option specifies that a string that will use "unsigned char" as //the type to store capacity or size internally. typedef string_options< stored_size >::type size_option_t; @@ -37,16 +41,36 @@ int main () bool exception_thrown = false; /*<-*/ #ifndef BOOST_NO_EXCEPTIONS - BOOST_CONTAINER_TRY{ size_optimized_string_t v(127, '\0');} BOOST_CONTAINER_CATCH(...) { exception_thrown = true; } BOOST_CONTAINER_CATCH_END + BOOST_CONTAINER_TRY{ size_optimized_string_t v(127, 'a');} BOOST_CONTAINER_CATCH(...) { exception_thrown = true; } BOOST_CONTAINER_CATCH_END #else exception_thrown = true; #endif //BOOST_NO_EXCEPTIONS /*->*/ - //=try { size_optimized_string_t v(127, '\0'); } + //=try { size_optimized_string_t v(127, 'a'); } //=catch(...){ exception_thrown = true; } assert(exception_thrown == true); +//-------------------------- +// Option "inline_chars" +//-------------------------- + + //This option specifies the capacity of the internally stored buffer + //The maximum value due to internal data organization is 127 chars + typedef string_options< inline_chars<100> >::type inline_chars_option_t; + typedef basic_string, void, inline_chars_option_t > inline100_string_t; + + //The size of the object will grow accordingly + assert(( sizeof(inline100_string_t) > sizeof(basic_string) )); + assert(( sizeof(inline100_string_t) > 100 )); + + //Internal capacity will be at least the specified one + assert((inline100_string_t().capacity() >= 100)); + +//-------------------------- +// Option "growth_factor" +//-------------------------- + //This option specifies that a string will increase its capacity 50% //each time the previous capacity was exhausted. typedef string_options< growth_factor >::type growth_50_option_t; diff --git a/include/boost/container/options.hpp b/include/boost/container/options.hpp index f73b0dd..b68e605 100644 --- a/include/boost/container/options.hpp +++ b/include/boost/container/options.hpp @@ -244,11 +244,12 @@ class default_next_capacity; typedef vector_opt vector_null_opt; -template +template struct string_opt { typedef GrowthType growth_factor_type; typedef StoredSizeType stored_size_type; + static const std::size_t inline_chars = InlineChars; template struct get_stored_size_type @@ -256,7 +257,7 @@ struct string_opt {}; }; -typedef string_opt string_null_opt; +typedef string_opt string_null_opt; struct growth_factor_50; @@ -695,6 +696,24 @@ using deque_options_t = typename boost::container::deque_options::ty #endif +//////////////////////////////////////////////////////////////// +// +// +// OPTIONS FOR STRING CONTAINER +// +// +//////////////////////////////////////////////////////////////// + +//! This option specifies the desired number of characters to be hold inline +//! in the container. +//! +//! A value zero represents the default value +//! (typically 10 chars in 32-bit systems and 22 chars in 64-bit systems). +//! +//!\tparam InlineChars An unsigned integer value. Values greater than 127 are not supported +//! duet to the internal data structure design. +BOOST_INTRUSIVE_OPTION_CONSTANT(inline_chars, std::size_t, InlineChars, inline_chars) + //! Helper metafunction to combine options into a single type to be used //! by \c boost::container::string. //! Supported options are: \c boost::container::growth_factor and \c boost::container::stored_size @@ -715,7 +734,9 @@ struct string_options #endif >::type packed_options; typedef string_opt< typename packed_options::growth_factor_type - , typename packed_options::stored_size_type> implementation_defined; + , typename packed_options::stored_size_type + , packed_options::inline_chars + > implementation_defined; /// @endcond typedef implementation_defined type; }; diff --git a/include/boost/container/string.hpp b/include/boost/container/string.hpp index 1f9c796..106823b 100644 --- a/include/boost/container/string.hpp +++ b/include/boost/container/string.hpp @@ -80,13 +80,14 @@ struct get_string_opt { typedef string_opt< typename default_if_void::type , typename default_if_void::type + , Options::inline_chars > type; }; template struct get_string_opt { - typedef string_opt type; + typedef string_opt type; }; namespace dtl { @@ -123,6 +124,8 @@ class basic_string_base typedef ::boost::intrusive::pointer_traits pointer_traits; + static const std::size_t inline_chars = options_type::inline_chars; + inline basic_string_base() : members_() {} @@ -209,24 +212,43 @@ class basic_string_base //This type has the same alignment and size as long_t but it's POD //so, unlike long_t, it can be placed in a union - typedef typename dtl::aligned_storage ::value>::type long_raw_t; + template + union short_type_test + { + long_raw_t l; + struct short_t + { + short_header h; + value_type data[NChar+1u]; + } s; + }; + protected: - BOOST_STATIC_CONSTEXPR size_type MinInternalBufferChars = 0; - BOOST_STATIC_CONSTEXPR size_type AlignmentOfValueType = alignment_of::value; - BOOST_STATIC_CONSTEXPR size_type ShortDataOffset = ((sizeof(short_header)-1)/AlignmentOfValueType+1)*AlignmentOfValueType; - BOOST_STATIC_CONSTEXPR size_type ZeroCostInternalBufferChars = - (sizeof(long_t) - ShortDataOffset)/sizeof(value_type); - BOOST_STATIC_CONSTEXPR size_type UnalignedFinalInternalBufferChars = - (ZeroCostInternalBufferChars > MinInternalBufferChars) ? - ZeroCostInternalBufferChars : MinInternalBufferChars; + //Due to internal data representation constraints, inline chars cannot be bigger than this + BOOST_STATIC_CONSTEXPR size_type MaxInlineChars = 127u; + BOOST_CONTAINER_STATIC_ASSERT(inline_chars <= MaxInlineChars); + + BOOST_STATIC_CONSTEXPR size_type SizeOfValueType = sizeof(value_type); + BOOST_STATIC_CONSTEXPR size_type ShortDataOffset = ((sizeof(short_header)-1)/SizeOfValueType+1)*SizeOfValueType; + BOOST_STATIC_CONSTEXPR size_type ZeroCostInternalStorage = (sizeof(long_t) - ShortDataOffset)/sizeof(value_type); + BOOST_STATIC_CONSTEXPR size_type InlineCharsStorage = (sizeof(short_type_test) - ShortDataOffset)/sizeof(value_type); + + //Select the biggest internal buffer between zero-cost SSO and user-requested SSO + BOOST_STATIC_CONSTEXPR size_type CandidateInternalStorageChars = + ZeroCostInternalStorage > InlineCharsStorage ? ZeroCostInternalStorage : InlineCharsStorage; + + //Clamp internal buffer chars to MaxInlineChars + BOOST_STATIC_CONSTEXPR size_type MaxInlineStorage = MaxInlineChars+1u; + BOOST_STATIC_CONSTEXPR size_type FinalInternalStorage = + CandidateInternalStorageChars < MaxInlineStorage ? CandidateInternalStorageChars : MaxInlineStorage; struct short_t { short_header h; - value_type data[UnalignedFinalInternalBufferChars]; + value_type data[FinalInternalStorage]; }; union repr_t_size_t @@ -282,7 +304,7 @@ class basic_string_base inline allocator_type &alloc() { return members_; } - BOOST_STATIC_CONSTEXPR size_type InternalBufferChars = (sizeof(repr_t) - ShortDataOffset)/sizeof(value_type); + BOOST_STATIC_CONSTEXPR size_type InternalBufferChars = FinalInternalStorage; private: @@ -581,6 +603,7 @@ class basic_string_base //! \tparam CharT The type of character it contains. //! \tparam Traits The Character Traits type, which encapsulates basic character operations //! \tparam Allocator The allocator, used for internal memory management. +//! \tparam Options A type produced from \c boost::container::string_options. #ifdef BOOST_CONTAINER_DOXYGEN_INVOKED template , class Allocator = void, class Options = void > #else