Skip to content

Commit 9e2afe8

Browse files
committed
Add "inline_chars" option to basic_string
1 parent 06cdd2f commit 9e2afe8

File tree

4 files changed

+95
-17
lines changed

4 files changed

+95
-17
lines changed

doc/container.qbk

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -882,6 +882,12 @@ the last template parameter and defined using the utility class
882882
small string optimization buffer might be reduced in size if a smaller than `std::size_t` `stored_size` unsigned
883883
type is used.
884884

885+
* [classref boost::container::inline_chars inline_chars]: the number of characters (excluding terminating null) that
886+
will be stored inside `basic_string` (small string optimization) before using dynamic allocation. Due to internal
887+
data structure limitations, this value must be equal or less than 127. The implementation will guarantee at least
888+
that number of characters in the internal buffer, but it might be more depending on alignment issues and the `stored_size`
889+
option.
890+
885891
See the following example to see how [classref boost::container::string_options string_options] can be
886892
used to customize `string_options`:
887893

@@ -1472,6 +1478,10 @@ use [*Boost.Container]? There are several reasons for that:
14721478
* Added C++20 `starts_with`/`ends_with` and C++23 `contains` overloads.
14731479
* Added null pointer checks for `basic_string` methods taking `const CharT*` parameters.
14741480
* Deleted constructor from `std::nullptr_t`.
1481+
* Added [classref boost::container::inline_chars inline_chars],
1482+
[classref boost::container::growth_factor growth_factor] and
1483+
[classref boost::container::stored_size stored_size] options to [classref boost::container::basic_string basic_string].
1484+
14751485
* Fixed bugs/issues:
14761486
* [@https://github.com/boostorg/container/issues/323 GitHub #323: ['"flat_tree::try_emplace UB"]].
14771487
* [@https://github.com/boostorg/container/issues/328 GitHub #328: ['"boost::container::deque stores a redundant copy of the allocator, increasing size"]].

example/doc_custom_string.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ int main ()
2020
{
2121
using namespace boost::container;
2222

23+
//--------------------------
24+
// Option "stored_size"
25+
//--------------------------
26+
2327
//This option specifies that a string that will use "unsigned char" as
2428
//the type to store capacity or size internally.
2529
typedef string_options< stored_size<unsigned char> >::type size_option_t;
@@ -37,16 +41,36 @@ int main ()
3741
bool exception_thrown = false;
3842
/*<-*/
3943
#ifndef BOOST_NO_EXCEPTIONS
40-
BOOST_CONTAINER_TRY{ size_optimized_string_t v(127, '\0');} BOOST_CONTAINER_CATCH(...) { exception_thrown = true; } BOOST_CONTAINER_CATCH_END
44+
BOOST_CONTAINER_TRY{ size_optimized_string_t v(127, 'a');} BOOST_CONTAINER_CATCH(...) { exception_thrown = true; } BOOST_CONTAINER_CATCH_END
4145
#else
4246
exception_thrown = true;
4347
#endif //BOOST_NO_EXCEPTIONS
4448
/*->*/
45-
//=try { size_optimized_string_t v(127, '\0'); }
49+
//=try { size_optimized_string_t v(127, 'a'); }
4650
//=catch(...){ exception_thrown = true; }
4751

4852
assert(exception_thrown == true);
4953

54+
//--------------------------
55+
// Option "inline_chars"
56+
//--------------------------
57+
58+
//This option specifies the capacity of the internally stored buffer
59+
//The maximum value due to internal data organization is 127 chars
60+
typedef string_options< inline_chars<100> >::type inline_chars_option_t;
61+
typedef basic_string<char, std::char_traits<char>, void, inline_chars_option_t > inline100_string_t;
62+
63+
//The size of the object will grow accordingly
64+
assert(( sizeof(inline100_string_t) > sizeof(basic_string<char>) ));
65+
assert(( sizeof(inline100_string_t) > 100 ));
66+
67+
//Internal capacity will be at least the specified one
68+
assert((inline100_string_t().capacity() >= 100));
69+
70+
//--------------------------
71+
// Option "growth_factor"
72+
//--------------------------
73+
5074
//This option specifies that a string will increase its capacity 50%
5175
//each time the previous capacity was exhausted.
5276
typedef string_options< growth_factor<growth_factor_50> >::type growth_50_option_t;

include/boost/container/options.hpp

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,19 +244,20 @@ class default_next_capacity;
244244

245245
typedef vector_opt<void, void> vector_null_opt;
246246

247-
template<class GrowthType, class StoredSizeType>
247+
template<class GrowthType, class StoredSizeType, std::size_t InlineChars>
248248
struct string_opt
249249
{
250250
typedef GrowthType growth_factor_type;
251251
typedef StoredSizeType stored_size_type;
252+
static const std::size_t inline_chars = InlineChars;
252253

253254
template<class AllocTraits>
254255
struct get_stored_size_type
255256
: get_stored_size_type_with_alloctraits<AllocTraits, StoredSizeType>
256257
{};
257258
};
258259

259-
typedef string_opt<void, void> string_null_opt;
260+
typedef string_opt<void, void, 0u> string_null_opt;
260261

261262
struct growth_factor_50;
262263

@@ -695,6 +696,24 @@ using deque_options_t = typename boost::container::deque_options<Options...>::ty
695696

696697
#endif
697698

699+
////////////////////////////////////////////////////////////////
700+
//
701+
//
702+
// OPTIONS FOR STRING CONTAINER
703+
//
704+
//
705+
////////////////////////////////////////////////////////////////
706+
707+
//! This option specifies the desired number of characters to be hold inline
708+
//! in the container.
709+
//!
710+
//! A value zero represents the default value
711+
//! (typically 10 chars in 32-bit systems and 22 chars in 64-bit systems).
712+
//!
713+
//!\tparam InlineChars An unsigned integer value. Values greater than 127 are not supported
714+
//! duet to the internal data structure design.
715+
BOOST_INTRUSIVE_OPTION_CONSTANT(inline_chars, std::size_t, InlineChars, inline_chars)
716+
698717
//! Helper metafunction to combine options into a single type to be used
699718
//! by \c boost::container::string.
700719
//! Supported options are: \c boost::container::growth_factor and \c boost::container::stored_size
@@ -715,7 +734,9 @@ struct string_options
715734
#endif
716735
>::type packed_options;
717736
typedef string_opt< typename packed_options::growth_factor_type
718-
, typename packed_options::stored_size_type> implementation_defined;
737+
, typename packed_options::stored_size_type
738+
, packed_options::inline_chars
739+
> implementation_defined;
719740
/// @endcond
720741
typedef implementation_defined type;
721742
};

include/boost/container/string.hpp

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,14 @@ struct get_string_opt
8080
{
8181
typedef string_opt< typename default_if_void<typename Options::growth_factor_type, growth_factor_60>::type
8282
, typename default_if_void<typename Options::stored_size_type, AllocatorSizeType>::type
83+
, Options::inline_chars
8384
> type;
8485
};
8586

8687
template<class AllocatorSizeType>
8788
struct get_string_opt<void, AllocatorSizeType>
8889
{
89-
typedef string_opt<growth_factor_60, AllocatorSizeType> type;
90+
typedef string_opt<growth_factor_60, AllocatorSizeType, 0u> type;
9091
};
9192

9293
namespace dtl {
@@ -123,6 +124,8 @@ class basic_string_base
123124

124125
typedef ::boost::intrusive::pointer_traits<pointer> pointer_traits;
125126

127+
static const std::size_t inline_chars = options_type::inline_chars;
128+
126129
inline basic_string_base()
127130
: members_()
128131
{}
@@ -209,24 +212,43 @@ class basic_string_base
209212

210213
//This type has the same alignment and size as long_t but it's POD
211214
//so, unlike long_t, it can be placed in a union
212-
213215
typedef typename dtl::aligned_storage
214216
<sizeof(long_t), dtl::alignment_of<long_t>::value>::type long_raw_t;
215217

218+
template <std::size_t NChar>
219+
union short_type_test
220+
{
221+
long_raw_t l;
222+
struct short_t
223+
{
224+
short_header h;
225+
value_type data[NChar+1u];
226+
} s;
227+
};
228+
216229
protected:
217-
BOOST_STATIC_CONSTEXPR size_type MinInternalBufferChars = 0;
218-
BOOST_STATIC_CONSTEXPR size_type AlignmentOfValueType = alignment_of<value_type>::value;
219-
BOOST_STATIC_CONSTEXPR size_type ShortDataOffset = ((sizeof(short_header)-1)/AlignmentOfValueType+1)*AlignmentOfValueType;
220-
BOOST_STATIC_CONSTEXPR size_type ZeroCostInternalBufferChars =
221-
(sizeof(long_t) - ShortDataOffset)/sizeof(value_type);
222-
BOOST_STATIC_CONSTEXPR size_type UnalignedFinalInternalBufferChars =
223-
(ZeroCostInternalBufferChars > MinInternalBufferChars) ?
224-
ZeroCostInternalBufferChars : MinInternalBufferChars;
230+
//Due to internal data representation constraints, inline chars cannot be bigger than this
231+
BOOST_STATIC_CONSTEXPR size_type MaxInlineChars = 127u;
232+
BOOST_CONTAINER_STATIC_ASSERT(inline_chars <= MaxInlineChars);
233+
234+
BOOST_STATIC_CONSTEXPR size_type SizeOfValueType = sizeof(value_type);
235+
BOOST_STATIC_CONSTEXPR size_type ShortDataOffset = ((sizeof(short_header)-1)/SizeOfValueType+1)*SizeOfValueType;
236+
BOOST_STATIC_CONSTEXPR size_type ZeroCostInternalStorage = (sizeof(long_t) - ShortDataOffset)/sizeof(value_type);
237+
BOOST_STATIC_CONSTEXPR size_type InlineCharsStorage = (sizeof(short_type_test<inline_chars>) - ShortDataOffset)/sizeof(value_type);
238+
239+
//Select the biggest internal buffer between zero-cost SSO and user-requested SSO
240+
BOOST_STATIC_CONSTEXPR size_type CandidateInternalStorageChars =
241+
ZeroCostInternalStorage > InlineCharsStorage ? ZeroCostInternalStorage : InlineCharsStorage;
242+
243+
//Clamp internal buffer chars to MaxInlineChars
244+
BOOST_STATIC_CONSTEXPR size_type MaxInlineStorage = MaxInlineChars+1u;
245+
BOOST_STATIC_CONSTEXPR size_type FinalInternalStorage =
246+
CandidateInternalStorageChars < MaxInlineStorage ? CandidateInternalStorageChars : MaxInlineStorage;
225247

226248
struct short_t
227249
{
228250
short_header h;
229-
value_type data[UnalignedFinalInternalBufferChars];
251+
value_type data[FinalInternalStorage];
230252
};
231253

232254
union repr_t_size_t
@@ -282,7 +304,7 @@ class basic_string_base
282304
inline allocator_type &alloc()
283305
{ return members_; }
284306

285-
BOOST_STATIC_CONSTEXPR size_type InternalBufferChars = (sizeof(repr_t) - ShortDataOffset)/sizeof(value_type);
307+
BOOST_STATIC_CONSTEXPR size_type InternalBufferChars = FinalInternalStorage;
286308

287309
private:
288310

@@ -581,6 +603,7 @@ class basic_string_base
581603
//! \tparam CharT The type of character it contains.
582604
//! \tparam Traits The Character Traits type, which encapsulates basic character operations
583605
//! \tparam Allocator The allocator, used for internal memory management.
606+
//! \tparam Options A type produced from \c boost::container::string_options.
584607
#ifdef BOOST_CONTAINER_DOXYGEN_INVOKED
585608
template <class CharT, class Traits = std::char_traits<CharT>, class Allocator = void, class Options = void >
586609
#else

0 commit comments

Comments
 (0)