Add "inline_chars" option to basic_string

This commit is contained in:
Ion Gaztañaga
2026-01-24 23:29:36 +01:00
parent 06cdd2fc4a
commit 9e2afe85ad
4 changed files with 95 additions and 17 deletions

View File

@@ -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"]].

View File

@@ -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<unsigned char> >::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<char, std::char_traits<char>, void, inline_chars_option_t > inline100_string_t;
//The size of the object will grow accordingly
assert(( sizeof(inline100_string_t) > sizeof(basic_string<char>) ));
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<growth_factor_50> >::type growth_50_option_t;

View File

@@ -244,11 +244,12 @@ class default_next_capacity;
typedef vector_opt<void, void> vector_null_opt;
template<class GrowthType, class StoredSizeType>
template<class GrowthType, class StoredSizeType, std::size_t InlineChars>
struct string_opt
{
typedef GrowthType growth_factor_type;
typedef StoredSizeType stored_size_type;
static const std::size_t inline_chars = InlineChars;
template<class AllocTraits>
struct get_stored_size_type
@@ -256,7 +257,7 @@ struct string_opt
{};
};
typedef string_opt<void, void> string_null_opt;
typedef string_opt<void, void, 0u> string_null_opt;
struct growth_factor_50;
@@ -695,6 +696,24 @@ using deque_options_t = typename boost::container::deque_options<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;
};

View File

@@ -80,13 +80,14 @@ struct get_string_opt
{
typedef string_opt< typename default_if_void<typename Options::growth_factor_type, growth_factor_60>::type
, typename default_if_void<typename Options::stored_size_type, AllocatorSizeType>::type
, Options::inline_chars
> type;
};
template<class AllocatorSizeType>
struct get_string_opt<void, AllocatorSizeType>
{
typedef string_opt<growth_factor_60, AllocatorSizeType> type;
typedef string_opt<growth_factor_60, AllocatorSizeType, 0u> type;
};
namespace dtl {
@@ -123,6 +124,8 @@ class basic_string_base
typedef ::boost::intrusive::pointer_traits<pointer> 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
<sizeof(long_t), dtl::alignment_of<long_t>::value>::type long_raw_t;
template <std::size_t NChar>
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_type>::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<inline_chars>) - 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 CharT, class Traits = std::char_traits<CharT>, class Allocator = void, class Options = void >
#else