28#if __has_include(<compare>)
33#include <unordered_map>
35#include <initializer_list>
40#include <fmt/format.h>
41#if FMT_VERSION >= 110000
42#include <fmt/ranges.h>
45#include <seastar/util/std-compat.hh>
46#include <seastar/util/modules.hh>
47#include <seastar/core/temporary_buffer.hh>
51SEASTAR_MODULE_EXPORT_BEGIN
53template <
typename char_type,
typename Size, Size max_size,
bool NulTerminate = true>
61using sstring = basic_sstring<char, uint32_t, 15>;
63using sstring = std::string;
66SEASTAR_MODULE_EXPORT_END
69[[noreturn]]
void throw_bad_alloc();
70[[noreturn]]
void throw_sstring_overflow();
71[[noreturn]]
void throw_sstring_out_of_range();
75template <
typename char_type,
typename Size, Size max_size,
bool NulTerminate>
78 (std::is_same_v<char_type, char>
79 || std::is_same_v<char_type, signed char>
80 || std::is_same_v<char_type, unsigned char>),
81 "basic_sstring only supports single byte char types");
89 char_type str[max_size];
93 static_assert(max_size <= 127,
"max_size too large");
95 bool is_internal() const noexcept {
96 return u.internal.size >= 0;
98 bool is_external() const noexcept {
99 return !is_internal();
101 const char_type* str() const noexcept {
102 return is_internal() ? u.internal.str : u.external.str;
104 char_type* str() noexcept {
105 return is_internal() ? u.internal.str : u.external.str;
109 using value_type = char_type;
110 using traits_type = std::char_traits<char_type>;
111 using allocator_type = std::allocator<char_type>;
112 using reference = char_type&;
113 using const_reference =
const char_type&;
114 using pointer = char_type*;
115 using const_pointer =
const char_type*;
116 using iterator = char_type*;
117 using const_iterator =
const char_type*;
119 using difference_type = ssize_t;
120 using size_type = Size;
121 static constexpr size_type npos =
static_cast<size_type
>(-1);
122 static constexpr unsigned padding() {
return unsigned(NulTerminate); }
129 u.internal.str[0] =
'\0';
132 basic_sstring(
const basic_sstring& x) {
133 if (x.is_internal()) {
134 u.internal = x.u.internal;
136 u.internal.size = -1;
137 u.external.str =
reinterpret_cast<char_type*
>(std::malloc(x.u.external.size + padding()));
138 if (!u.external.str) {
139 internal::throw_bad_alloc();
141 std::copy(x.u.external.str, x.u.external.str + x.u.external.size + padding(), u.external.str);
142 u.external.size = x.u.external.size;
145 basic_sstring(basic_sstring&& x)
noexcept {
146#pragma GCC diagnostic push
150#pragma GCC diagnostic ignored "-Wuninitialized"
152#pragma GCC diagnostic pop
153 x.u.internal.size = 0;
154 x.u.internal.str[0] =
'\0';
156 basic_sstring(initialized_later,
size_t size) {
157 if (size_type(size) != size) {
158 internal::throw_sstring_overflow();
160 if (size + padding() <=
sizeof(u.internal.str)) {
162 u.internal.str[size] =
'\0';
164 u.internal.size = size;
166 u.internal.size = -1;
167 u.external.str =
reinterpret_cast<char_type*
>(std::malloc(size + padding()));
168 if (!u.external.str) {
169 internal::throw_bad_alloc();
171 u.external.size = size;
173 u.external.str[size] =
'\0';
177 basic_sstring(
const char_type* x,
size_t size) {
178 if (size_type(size) != size) {
179 internal::throw_sstring_overflow();
181 if (size + padding() <=
sizeof(u.internal.str)) {
182 std::copy(x, x + size, u.internal.str);
184 u.internal.str[size] =
'\0';
186 u.internal.size = size;
188 u.internal.size = -1;
189 u.external.str =
reinterpret_cast<char_type*
>(std::malloc(size + padding()));
190 if (!u.external.str) {
191 internal::throw_bad_alloc();
193 u.external.size = size;
194 std::copy(x, x + size, u.external.str);
196 u.external.str[size] =
'\0';
201 basic_sstring(
size_t size, char_type x) : basic_sstring(initialized_later(), size) {
202 memset(begin(), x, size);
205 basic_sstring(
const char* x) : basic_sstring(reinterpret_cast<const char_type*>(x),
std::strlen(x)) {}
206 basic_sstring(std::basic_string<char_type>& x) : basic_sstring(x.c_str(), x.size()) {}
207 basic_sstring(std::initializer_list<char_type> x) : basic_sstring(x.begin(), x.end() - x.begin()) {}
208 basic_sstring(
const char_type* b,
const char_type* e) : basic_sstring(b, e - b) {}
209 basic_sstring(
const std::basic_string<char_type>& s)
210 : basic_sstring(s.data(), s.size()) {}
211 template <
typename InputIterator>
212 basic_sstring(InputIterator first, InputIterator last)
213 : basic_sstring(initialized_later(),
std::distance(first, last)) {
214 std::copy(first, last, begin());
216 explicit basic_sstring(std::basic_string_view<char_type, traits_type> v)
217 : basic_sstring(v.data(), v.size()) {
219 ~basic_sstring() noexcept {
221 std::free(u.external.str);
224 basic_sstring& operator=(
const basic_sstring& x) {
225 basic_sstring tmp(x);
229 basic_sstring& operator=(basic_sstring&& x)
noexcept {
231 this->~basic_sstring();
232 new (
this) basic_sstring(std::move(x));
236 operator std::basic_string<char_type>()
const {
237 return { str(), size() };
240 size_t size() const noexcept {
241 return is_internal() ? u.internal.size : u.external.size;
244 size_t length() const noexcept {
248 size_t find(char_type t,
size_t pos = 0) const noexcept {
249 const char_type* it = str() + pos;
250 const char_type* end = str() + size();
260 size_t find(
const char_type* c_str,
size_t pos,
size_t len2)
const noexcept {
261 assert(c_str !=
nullptr || len2 == 0);
270 const char_type* it = str() + pos;
271 const char_type* end = str() + size();
272 size_t len1 = end - it;
277 char_type f2 = *c_str;
285 it = traits_type::find(it, len1 - len2 + 1, f2);
290 if (traits_type::compare(it, c_str, len2) == 0) {
298 constexpr size_t find(
const char_type* s,
size_t pos = 0) const noexcept {
299 return find(s, pos, traits_type::length(s));
302 size_t find(
const basic_sstring& s,
size_t pos = 0) const noexcept {
303 return find(s.str(), pos, s.size());
306 template<
class StringViewLike,
307 std::enable_if_t<std::is_convertible_v<StringViewLike,
308 std::basic_string_view<char_type, traits_type>>,
310 size_t find(
const StringViewLike& sv_like, size_type pos = 0) const noexcept {
311 std::basic_string_view<char_type, traits_type> sv = sv_like;
312 return find(sv.data(), pos, sv.size());
322 const char_type* str_start = str();
327 const char_type* p = str_start + pos + 1;
331 return (p - str_start);
333 }
while (p != str_start);
346 std::copy(begin(), end(), ret.begin());
347 std::copy(s, s + n, ret.begin() + size());
348 *
this = std::move(ret);
357 template <
class Operation>
358 requires std::is_invocable_r_v<size_t, Operation, char_type*, size_t>
363 size_t r = std::move(op)(data(), n);
373 void resize(
size_t n,
const char_type c =
'\0') {
376 }
else if (n < size()) {
380 u.internal.str[n] =
'\0';
382 }
else if (n + padding() <=
sizeof(u.internal.str)) {
387 u.external.str[n] =
'\0';
400 internal::throw_sstring_out_of_range();
403 if (n1 > size() - pos) {
409 std::copy(s, s + n2, begin() + pos);
414 char_type* p= ret.begin();
415 std::copy(begin(), begin() + pos, p);
418 std::copy(s, s + n2, p);
421 std::copy(begin() + pos + n1, end(), p);
422 *
this = std::move(ret);
426 template <
class InputIterator>
428 InputIterator first, InputIterator last) {
429 if (i1 < begin() || i1 > end() || i2 < begin()) {
430 internal::throw_sstring_out_of_range();
436 if (i2 - i1 == last - first) {
438 std::copy(first, last,
const_cast<char_type*
>(i1));
441 basic_sstring ret(initialized_later(), size() + (last - first) - (i2 - i1));
442 char_type* p = ret.begin();
443 p = std::copy(cbegin(), i1, p);
444 p = std::copy(first, last, p);
445 std::copy(i2, cend(), p);
446 *
this = std::move(ret);
450 iterator erase(iterator first, iterator last) {
451 size_t pos = first - begin();
452 replace(pos, last - first,
nullptr, 0);
453 return begin() + pos;
460 template <
class InputIterator>
461 void insert(const_iterator p, InputIterator beg, InputIterator end) {
495 return operator[](size() - 1);
505 return operator[](size() - 1);
510 internal::throw_sstring_out_of_range();
512 if (len > size() - from) {
518 return { str() + from , len };
521 const char_type& at(
size_t pos)
const {
523 internal::throw_sstring_out_of_range();
525 return *(str() + pos);
528 char_type& at(
size_t pos) {
530 internal::throw_sstring_out_of_range();
532 return *(str() + pos);
535 bool empty() const noexcept {
536 return u.internal.size == 0;
540 [[deprecated(
"Use = {}")]]
541 void reset() noexcept {
543 std::free(u.external.str);
547 u.internal.str[0] =
'\0';
550 temporary_buffer<char_type> release() && {
552 auto ptr = u.external.str;
553 auto size = u.external.size;
554 u.external.str =
nullptr;
556 return temporary_buffer<char_type>(ptr, size, make_free_deleter(ptr));
558 auto buf = temporary_buffer<char_type>(u.internal.size);
559 std::copy(u.internal.str, u.internal.str + u.internal.size, buf.get_write());
562 u.internal.str[0] =
'\0';
567 int compare(std::basic_string_view<char_type, traits_type> x)
const noexcept {
568 auto n = traits_type::compare(begin(), x.begin(), std::min(size(), x.size()));
572 if (size() < x.size()) {
574 }
else if (size() > x.size()) {
581 int compare(
size_t pos,
size_t sz, std::basic_string_view<char_type, traits_type> x)
const {
583 internal::throw_sstring_out_of_range();
586 sz = std::min(size() - pos, sz);
587 auto n = traits_type::compare(begin() + pos, x.begin(), std::min(sz, x.size()));
593 }
else if (sz > x.size()) {
600 constexpr bool starts_with(std::basic_string_view<char_type, traits_type> sv)
const noexcept {
601 return size() >= sv.size() && compare(0, sv.size(), sv) == 0;
604 constexpr bool starts_with(char_type c)
const noexcept {
605 return !empty() && traits_type::eq(
front(), c);
608 constexpr bool starts_with(
const char_type* s)
const noexcept {
609 return starts_with(std::basic_string_view<char_type, traits_type>(s));
612 constexpr bool ends_with(std::basic_string_view<char_type, traits_type> sv)
const noexcept {
613 return size() >= sv.size() && compare(size() - sv.size(), npos, sv) == 0;
616 constexpr bool ends_with(char_type c)
const noexcept {
617 return !empty() && traits_type::eq(
back(), c);
620 constexpr bool ends_with(
const char_type* s)
const noexcept {
621 return ends_with(std::basic_string_view<char_type, traits_type>(s));
624 constexpr bool contains(std::basic_string_view<char_type, traits_type> sv)
const noexcept {
625 return find(sv) != npos;
628 constexpr bool contains(char_type c)
const noexcept {
629 return find(c) != npos;
632 constexpr bool contains(
const char_type* s)
const noexcept {
633 return find(s) != npos;
636 void swap(basic_sstring& x)
noexcept {
642 char_type* data() noexcept {
645 const char_type* data() const noexcept {
648 const char_type* c_str() const noexcept {
651 const char_type* begin() const noexcept {
return str(); }
652 const char_type* end() const noexcept {
return str() + size(); }
653 const char_type* cbegin() const noexcept {
return str(); }
654 const char_type* cend() const noexcept {
return str() + size(); }
655 char_type* begin() noexcept {
return str(); }
656 char_type* end() noexcept {
return str() + size(); }
657 bool operator==(
const basic_sstring& x)
const noexcept {
658 return size() == x.size() && std::equal(begin(), end(), x.begin());
660 bool operator!=(
const basic_sstring& x)
const noexcept {
661 return !operator==(x);
663 constexpr bool operator==(
const std::basic_string<char_type> x)
const noexcept {
664 return compare(x) == 0;
666 constexpr bool operator==(
const char_type* x)
const noexcept {
667 return compare(x) == 0;
669#if __cpp_lib_three_way_comparison
670 constexpr std::strong_ordering operator<=>(
const auto& x)
const noexcept {
671 return compare(x) <=> 0;
674 bool operator<(
const basic_sstring& x)
const noexcept {
675 return compare(x) < 0;
678 basic_sstring operator+(
const basic_sstring& x)
const {
679 basic_sstring ret(initialized_later(), size() + x.size());
680 std::copy(begin(), end(), ret.begin());
681 std::copy(x.begin(), x.end(), ret.begin() + size());
684 basic_sstring& operator+=(
const basic_sstring& x) {
685 return *
this = *
this + x;
687 char_type& operator[](size_type pos)
noexcept {
690 const char_type& operator[](size_type pos)
const noexcept {
694 operator std::basic_string_view<char_type>() const noexcept {
703 static_assert(
noexcept(std::basic_string_view<char_type>(str(), size())));
704 return std::basic_string_view<char_type>(str(), size());
709template <
class T>
struct is_sstring : std::false_type {};
710template <
typename char_type,
typename Size, Size max_size,
bool NulTerminate>
711struct is_sstring<basic_sstring<char_type, Size, max_size, NulTerminate>> : std::true_type {};
715template <
typename string_type = s
string>
716string_type uninitialized_string(
size_t size) {
717 if constexpr (internal::is_sstring<string_type>::value) {
718 return string_type(
typename string_type::initialized_later(), size);
721#ifdef __cpp_lib_string_resize_and_overwrite
722 ret.resize_and_overwrite(size, [](
typename string_type::value_type*,
typename string_type::size_type n) {
return n; });
731template <
typename char_type,
typename size_type,
size_type Max,
size_type N,
bool NulTerminate>
733basic_sstring<char_type, size_type, Max, NulTerminate>
734operator+(
const char(&s)[N],
const basic_sstring<char_type, size_type, Max, NulTerminate>& t) {
735 using sstring = basic_sstring<char_type, size_type, Max, NulTerminate>;
737 sstring ret(
typename sstring::initialized_later(), N-1 + t.size());
738 auto p = std::copy(std::begin(s), std::end(s)-1, ret.begin());
739 std::copy(t.begin(), t.end(), p);
745size_t constexpr str_len(
const T& s) {
746 return std::string_view(s).size();
750template <
typename char_type,
typename size_type,
size_type max_size>
752void swap(basic_sstring<char_type, size_type, max_size>& x,
753 basic_sstring<char_type, size_type, max_size>& y)
noexcept
759template <
typename char_type,
typename size_type,
size_type max_size,
bool NulTerminate,
typename char_traits>
761std::basic_ostream<char_type, char_traits>&
762operator<<(std::basic_ostream<char_type, char_traits>& os,
763 const basic_sstring<char_type, size_type, max_size, NulTerminate>& s) {
764 return os.write(s.begin(), s.size());
768template <
typename char_type,
typename size_type,
size_type max_size,
bool NulTerminate,
typename char_traits>
770std::basic_istream<char_type, char_traits>&
771operator>>(std::basic_istream<char_type, char_traits>& is,
772 basic_sstring<char_type, size_type, max_size, NulTerminate>& s) {
784template <
typename char_type,
typename size_type,
size_type max_size,
bool NulTerminate>
785struct hash<
seastar::basic_sstring<char_type, size_type, max_size, NulTerminate>> {
787 return std::hash<std::basic_string_view<char_type>>()(s);
797void copy_str_to(
char*& dst,
const T& s) {
798 std::string_view v(s);
799 dst = std::copy(v.begin(), v.end(), dst);
802template <
typename String = sstring,
typename... Args>
803static String make_sstring(Args&&... args)
805 String ret = uninitialized_string<String>((str_len(args) + ...));
806 auto dst = ret.data();
807 (copy_str_to(dst, args), ...);
812template <
typename string_type,
typename T>
813string_type to_sstring(T value) {
814 auto size = fmt::formatted_size(
"{}", value);
815 auto formatted = uninitialized_string<string_type>(size);
816 fmt::format_to(formatted.data(),
"{}", value);
820template <
typename string_type>
821string_type to_sstring(
const char* value) {
822 return string_type(value);
825template <
typename string_type>
826string_type to_sstring(sstring value) {
830template <
typename string_type>
831string_type to_sstring(
const temporary_buffer<char>& buf) {
832 return string_type(buf.get(), buf.size());
836template <
typename string_type = s
string,
typename T>
837string_type to_sstring(T value) {
838 return internal::to_sstring<string_type>(value);
842#ifdef SEASTAR_DEPRECATED_OSTREAM_FORMATTERS
848[[deprecated(
"Use {fmt} instead")]]
850std::ostream&
operator<<(std::ostream& os,
const std::vector<T>& v) {
853 for (
auto&& elem : v) {
866template <
typename Key,
typename T,
typename Hash,
typename KeyEqual,
typename Allocator>
867[[deprecated(
"Use {fmt} instead")]]
868std::ostream&
operator<<(std::ostream& os,
const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v) {
871 for (
auto&& elem : v) {
877 os <<
"{" << elem.first <<
" -> " << elem.second <<
"}";
886#if FMT_VERSION >= 110000
888template <
typename char_type,
typename Size, Size max_size,
bool NulTerminate>
889struct fmt::range_format_kind<
seastar::basic_sstring<char_type, Size, max_size, NulTerminate>, char_type> : std::integral_constant<fmt::range_format, fmt::range_format::disabled>
894#if FMT_VERSION >= 90000
900template <
typename char_type,
typename Size, Size max_size,
bool NulTerminate>
901struct fmt::formatter<
seastar::basic_sstring<char_type, Size, max_size, NulTerminate>>
902 :
public fmt::formatter<basic_string_view<char_type>> {
903 using format_as_t = basic_string_view<char_type>;
904 using base = fmt::formatter<format_as_t>;
905 template <
typename FormatContext>
906 auto format(const ::seastar::basic_sstring<char_type, Size, max_size, NulTerminate>& s, FormatContext& ctx)
const {
907 return base::format(format_as_t{s.c_str(), s.size()}, ctx);
Definition: sstring.hh:76
reference back() noexcept
Definition: sstring.hh:494
basic_sstring & replace(size_type pos, size_type n1, const char_type *s, size_type n2)
Definition: sstring.hh:397
const_reference back() const noexcept
Definition: sstring.hh:504
const_reference front() const noexcept
Definition: sstring.hh:483
void insert(const_iterator p, InputIterator beg, InputIterator end)
Definition: sstring.hh:461
void resize(size_t n, const char_type c='\0')
Definition: sstring.hh:373
reference front() noexcept
Definition: sstring.hh:472
basic_sstring & append(const char_type *s, size_t n)
Definition: sstring.hh:344
size_t find_last_of(char_type c, size_t pos=npos) const noexcept
Definition: sstring.hh:321
void resize_and_overwrite(size_t n, Operation op)
Definition: sstring.hh:359
Definition: sstring.hh:124
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
sstring format(fmt::format_string< A... > fmt, A &&... a)
Definition: format.hh:42
ostream & operator<<(ostream &os, const seastar::lazy_eval< Func > &lf)
Definition: lazy.hh:131
Definition: sstring.hh:83
Definition: sstring.hh:88