24 #ifndef SEASTAR_MODULE
28 #if __has_include(<compare>)
33 #include <unordered_map>
36 #include <initializer_list>
40 #include <type_traits>
41 #include <fmt/ostream.h>
43 #include <seastar/util/concepts.hh>
44 #include <seastar/util/std-compat.hh>
45 #include <seastar/util/modules.hh>
46 #include <seastar/core/temporary_buffer.hh>
50 SEASTAR_MODULE_EXPORT_BEGIN
52 template <
typename char_type,
typename Size, Size max_size,
bool NulTerminate = true>
55 #ifdef SEASTAR_SSTRING
60 using sstring = basic_sstring<char, uint32_t, 15>;
62 using sstring = std::string;
65 SEASTAR_MODULE_EXPORT_END
68 [[noreturn]]
void throw_bad_alloc();
69 [[noreturn]]
void throw_sstring_overflow();
70 [[noreturn]]
void throw_sstring_out_of_range();
74 template <
typename char_type,
typename Size, Size max_size,
bool NulTerminate>
77 (std::is_same_v<char_type, char>
78 || std::is_same_v<char_type, signed char>
79 || std::is_same_v<char_type, unsigned char>),
80 "basic_sstring only supports single byte char types");
88 char_type str[max_size];
92 static_assert(max_size <= 127,
"max_size too large");
94 bool is_internal() const noexcept {
95 return u.internal.size >= 0;
97 bool is_external() const noexcept {
98 return !is_internal();
100 const char_type* str() const noexcept {
101 return is_internal() ? u.internal.str : u.external.str;
103 char_type* str() noexcept {
104 return is_internal() ? u.internal.str : u.external.str;
108 using value_type = char_type;
109 using traits_type = std::char_traits<char_type>;
110 using allocator_type = std::allocator<char_type>;
111 using reference = char_type&;
112 using const_reference =
const char_type&;
113 using pointer = char_type*;
114 using const_pointer =
const char_type*;
115 using iterator = char_type*;
116 using const_iterator =
const char_type*;
118 using difference_type = ssize_t;
119 using size_type = Size;
120 static constexpr size_type npos =
static_cast<size_type
>(-1);
121 static constexpr
unsigned padding() {
return unsigned(NulTerminate); }
128 u.internal.str[0] =
'\0';
131 basic_sstring(
const basic_sstring& x) {
132 if (x.is_internal()) {
133 u.internal = x.u.internal;
135 u.internal.size = -1;
136 u.external.str =
reinterpret_cast<char_type*
>(std::malloc(x.u.external.size + padding()));
137 if (!u.external.str) {
138 internal::throw_bad_alloc();
140 std::copy(x.u.external.str, x.u.external.str + x.u.external.size + padding(), u.external.str);
141 u.external.size = x.u.external.size;
144 basic_sstring(basic_sstring&& x) noexcept {
145 #pragma GCC diagnostic push
149 #pragma GCC diagnostic ignored "-Wuninitialized"
151 #pragma GCC diagnostic pop
152 x.u.internal.size = 0;
153 x.u.internal.str[0] =
'\0';
155 basic_sstring(initialized_later,
size_t size) {
156 if (size_type(size) != size) {
157 internal::throw_sstring_overflow();
159 if (size + padding() <=
sizeof(u.internal.str)) {
161 u.internal.str[size] =
'\0';
163 u.internal.size = size;
165 u.internal.size = -1;
166 u.external.str =
reinterpret_cast<char_type*
>(std::malloc(size + padding()));
167 if (!u.external.str) {
168 internal::throw_bad_alloc();
170 u.external.size = size;
172 u.external.str[size] =
'\0';
176 basic_sstring(
const char_type* x,
size_t size) {
177 if (size_type(size) != size) {
178 internal::throw_sstring_overflow();
180 if (size + padding() <=
sizeof(u.internal.str)) {
181 std::copy(x, x + size, u.internal.str);
183 u.internal.str[size] =
'\0';
185 u.internal.size = size;
187 u.internal.size = -1;
188 u.external.str =
reinterpret_cast<char_type*
>(std::malloc(size + padding()));
189 if (!u.external.str) {
190 internal::throw_bad_alloc();
192 u.external.size = size;
193 std::copy(x, x + size, u.external.str);
195 u.external.str[size] =
'\0';
200 basic_sstring(
size_t size, char_type x) : basic_sstring(initialized_later(), size) {
201 memset(begin(), x, size);
204 basic_sstring(
const char* x) : basic_sstring(reinterpret_cast<const char_type*>(x), std::strlen(x)) {}
205 basic_sstring(std::basic_string<char_type>& x) : basic_sstring(x.c_str(), x.size()) {}
206 basic_sstring(std::initializer_list<char_type> x) : basic_sstring(x.begin(), x.end() - x.begin()) {}
207 basic_sstring(
const char_type* b,
const char_type* e) : basic_sstring(b, e - b) {}
208 basic_sstring(
const std::basic_string<char_type>& s)
209 : basic_sstring(s.data(), s.size()) {}
210 template <
typename InputIterator>
211 basic_sstring(InputIterator first, InputIterator last)
212 : basic_sstring(initialized_later(), std::distance(first, last)) {
213 std::copy(first, last, begin());
215 explicit basic_sstring(std::basic_string_view<char_type, traits_type> v)
216 : basic_sstring(v.data(), v.size()) {
218 ~basic_sstring() noexcept {
220 std::free(u.external.str);
223 basic_sstring& operator=(
const basic_sstring& x) {
224 basic_sstring tmp(x);
228 basic_sstring& operator=(basic_sstring&& x) noexcept {
230 this->~basic_sstring();
231 new (
this) basic_sstring(std::move(x));
235 operator std::basic_string<char_type>()
const {
236 return { str(), size() };
239 size_t size() const noexcept {
240 return is_internal() ? u.internal.size : u.external.size;
243 size_t length() const noexcept {
247 size_t find(char_type t,
size_t pos = 0) const noexcept {
248 const char_type* it = str() + pos;
249 const char_type* end = str() + size();
259 size_t find(
const char_type* c_str,
size_t pos,
size_t len2)
const noexcept {
260 assert(c_str !=
nullptr || len2 == 0);
269 const char_type* it = str() + pos;
270 const char_type* end = str() + size();
271 size_t len1 = end - it;
276 char_type f2 = *c_str;
284 it = traits_type::find(it, len1 - len2 + 1, f2);
289 if (traits_type::compare(it, c_str, len2) == 0) {
297 constexpr
size_t find(
const char_type* s,
size_t pos = 0) const noexcept {
298 return find(s, pos, traits_type::length(s));
301 size_t find(
const basic_sstring& s,
size_t pos = 0) const noexcept {
302 return find(s.str(), pos, s.size());
305 template<
class StringViewLike,
306 std::enable_if_t<std::is_convertible_v<StringViewLike,
307 std::basic_string_view<char_type, traits_type>>,
309 size_t find(
const StringViewLike& sv_like, size_type pos = 0) const noexcept {
310 std::basic_string_view<char_type, traits_type> sv = sv_like;
311 return find(sv.data(), pos, sv.size());
321 const char_type* str_start = str();
326 const char_type* p = str_start + pos + 1;
330 return (p - str_start);
332 }
while (p != str_start);
345 std::copy(begin(), end(), ret.begin());
346 std::copy(s, s + n, ret.begin() + size());
347 *
this = std::move(ret);
356 template <
class Operation>
357 SEASTAR_CONCEPT( requires std::is_invocable_r_v<size_t, Operation, char_type*, size_t> )
362 size_t r = std::move(op)(data(), n);
372 void resize(
size_t n,
const char_type c =
'\0') {
375 }
else if (n < size()) {
379 u.internal.str[n] =
'\0';
381 }
else if (n + padding() <=
sizeof(u.internal.str)) {
386 u.external.str[n] =
'\0';
399 internal::throw_sstring_out_of_range();
402 if (n1 > size() - pos) {
408 std::copy(s, s + n2, begin() + pos);
413 char_type* p= ret.begin();
414 std::copy(begin(), begin() + pos, p);
417 std::copy(s, s + n2, p);
420 std::copy(begin() + pos + n1, end(), p);
421 *
this = std::move(ret);
425 template <
class InputIterator>
427 InputIterator first, InputIterator last) {
428 if (i1 < begin() || i1 > end() || i2 < begin()) {
429 internal::throw_sstring_out_of_range();
435 if (i2 - i1 == last - first) {
437 std::copy(first, last,
const_cast<char_type*
>(i1));
440 basic_sstring ret(initialized_later(), size() + (last - first) - (i2 - i1));
441 char_type* p = ret.begin();
442 p = std::copy(cbegin(), i1, p);
443 p = std::copy(first, last, p);
444 std::copy(i2, cend(), p);
445 *
this = std::move(ret);
449 iterator erase(iterator first, iterator last) {
450 size_t pos = first - begin();
451 replace(pos, last - first,
nullptr, 0);
452 return begin() + pos;
459 template <
class InputIterator>
460 void insert(const_iterator p, InputIterator beg, InputIterator end) {
494 return operator[](size() - 1);
504 return operator[](size() - 1);
509 internal::throw_sstring_out_of_range();
511 if (len > size() - from) {
517 return { str() + from , len };
520 const char_type& at(
size_t pos)
const {
522 internal::throw_sstring_out_of_range();
524 return *(str() + pos);
527 char_type& at(
size_t pos) {
529 internal::throw_sstring_out_of_range();
531 return *(str() + pos);
534 bool empty() const noexcept {
535 return u.internal.size == 0;
539 [[deprecated(
"Use = {}")]]
540 void reset() noexcept {
542 std::free(u.external.str);
546 u.internal.str[0] =
'\0';
549 temporary_buffer<char_type> release() && {
551 auto ptr = u.external.str;
552 auto size = u.external.size;
553 u.external.str =
nullptr;
555 return temporary_buffer<char_type>(ptr, size, make_free_deleter(ptr));
557 auto buf = temporary_buffer<char_type>(u.internal.size);
558 std::copy(u.internal.str, u.internal.str + u.internal.size, buf.get_write());
561 u.internal.str[0] =
'\0';
566 int compare(std::basic_string_view<char_type, traits_type> x)
const noexcept {
567 auto n = traits_type::compare(begin(), x.begin(), std::min(size(), x.size()));
571 if (size() < x.size()) {
573 }
else if (size() > x.size()) {
580 int compare(
size_t pos,
size_t sz, std::basic_string_view<char_type, traits_type> x)
const {
582 internal::throw_sstring_out_of_range();
585 sz = std::min(size() - pos, sz);
586 auto n = traits_type::compare(begin() + pos, x.begin(), std::min(sz, x.size()));
592 }
else if (sz > x.size()) {
599 constexpr
bool starts_with(std::basic_string_view<char_type, traits_type> sv)
const noexcept {
600 return size() > sv.size() && compare(0, sv.size(), sv) == 0;
603 constexpr
bool starts_with(char_type c)
const noexcept {
604 return !empty() && traits_type::eq(
front(), c);
607 constexpr
bool starts_with(
const char_type* s)
const noexcept {
608 return starts_with(std::basic_string_view<char_type, traits_type>(s));
611 constexpr
bool ends_with(std::basic_string_view<char_type, traits_type> sv)
const noexcept {
612 return size() > sv.size() && compare(size() - sv.size(), npos, sv) == 0;
615 constexpr
bool ends_with(char_type c)
const noexcept {
616 return !empty() && traits_type::eq(
back(), c);
619 constexpr
bool ends_with(
const char_type* s)
const noexcept {
620 return ends_with(std::basic_string_view<char_type, traits_type>(s));
623 constexpr
bool contains(std::basic_string_view<char_type, traits_type> sv)
const noexcept {
624 return find(sv) != npos;
627 constexpr
bool contains(char_type c)
const noexcept {
628 return find(c) != npos;
631 constexpr
bool contains(
const char_type* s)
const noexcept {
632 return find(s) != npos;
635 void swap(basic_sstring& x) noexcept {
641 char_type* data() noexcept {
644 const char_type* data() const noexcept {
647 const char_type* c_str() const noexcept {
650 const char_type* begin() const noexcept {
return str(); }
651 const char_type* end() const noexcept {
return str() + size(); }
652 const char_type* cbegin() const noexcept {
return str(); }
653 const char_type* cend() const noexcept {
return str() + size(); }
654 char_type* begin() noexcept {
return str(); }
655 char_type* end() noexcept {
return str() + size(); }
656 bool operator==(
const basic_sstring& x)
const noexcept {
657 return size() == x.size() && std::equal(begin(), end(), x.begin());
659 bool operator!=(
const basic_sstring& x)
const noexcept {
660 return !operator==(x);
662 constexpr
bool operator==(
const std::basic_string<char_type> x)
const noexcept {
663 return compare(x) == 0;
665 constexpr
bool operator==(
const char_type* x)
const noexcept {
666 return compare(x) == 0;
668 #if __cpp_lib_three_way_comparison
669 constexpr std::strong_ordering operator<=>(
const auto& x)
const noexcept {
670 return compare(x) <=> 0;
673 bool operator<(
const basic_sstring& x)
const noexcept {
674 return compare(x) < 0;
677 basic_sstring operator+(
const basic_sstring& x)
const {
678 basic_sstring ret(initialized_later(), size() + x.size());
679 std::copy(begin(), end(), ret.begin());
680 std::copy(x.begin(), x.end(), ret.begin() + size());
683 basic_sstring& operator+=(
const basic_sstring& x) {
684 return *
this = *
this + x;
686 char_type& operator[](size_type pos) noexcept {
689 const char_type& operator[](size_type pos)
const noexcept {
693 operator std::basic_string_view<char_type>() const noexcept {
702 static_assert(noexcept(std::basic_string_view<char_type>(str(), size())));
703 return std::basic_string_view<char_type>(str(), size());
708 template <
class T>
struct is_sstring : std::false_type {};
709 template <
typename char_type,
typename Size, Size max_size,
bool NulTerminate>
710 struct is_sstring<basic_sstring<char_type, Size, max_size, NulTerminate>> : std::true_type {};
713 SEASTAR_MODULE_EXPORT
714 template <
typename string_type = s
string>
715 string_type uninitialized_string(
size_t size) {
716 if constexpr (internal::is_sstring<string_type>::value) {
717 return string_type(
typename string_type::initialized_later(), size);
720 #ifdef __cpp_lib_string_resize_and_overwrite
721 ret.resize_and_overwrite(size, [](string_type::value_type*, string_type::size_type n) {
return n; });
729 SEASTAR_MODULE_EXPORT
730 template <
typename char_type,
typename size_type,
size_type Max,
size_type N,
bool NulTerminate>
732 basic_sstring<char_type, size_type, Max, NulTerminate>
733 operator+(
const char(&s)[N],
const basic_sstring<char_type, size_type, Max, NulTerminate>& t) {
734 using sstring = basic_sstring<char_type, size_type, Max, NulTerminate>;
736 sstring ret(
typename sstring::initialized_later(), N-1 + t.size());
737 auto p = std::copy(std::begin(s), std::end(s)-1, ret.begin());
738 std::copy(t.begin(), t.end(), p);
742 template <
typename T>
744 size_t constexpr str_len(
const T& s) {
745 return std::string_view(s).size();
748 SEASTAR_MODULE_EXPORT
749 template <
typename char_type,
typename size_type,
size_type max_size>
751 void swap(basic_sstring<char_type, size_type, max_size>& x,
752 basic_sstring<char_type, size_type, max_size>& y) noexcept
757 SEASTAR_MODULE_EXPORT
758 template <
typename char_type,
typename size_type,
size_type max_size,
bool NulTerminate,
typename char_traits>
760 std::basic_ostream<char_type, char_traits>&
761 operator<<(std::basic_ostream<char_type, char_traits>& os,
762 const basic_sstring<char_type, size_type, max_size, NulTerminate>& s) {
763 return os.write(s.begin(), s.size());
766 SEASTAR_MODULE_EXPORT
767 template <
typename char_type,
typename size_type,
size_type max_size,
bool NulTerminate,
typename char_traits>
769 std::basic_istream<char_type, char_traits>&
770 operator>>(std::basic_istream<char_type, char_traits>& is,
771 basic_sstring<char_type, size_type, max_size, NulTerminate>& s) {
782 SEASTAR_MODULE_EXPORT
783 template <
typename char_type,
typename size_type,
size_type max_size,
bool NulTerminate>
784 struct hash<
seastar::basic_sstring<char_type, size_type, max_size, NulTerminate>> {
786 return std::hash<std::basic_string_view<char_type>>()(s);
794 template <
typename T>
796 void copy_str_to(
char*& dst,
const T& s) {
797 std::string_view v(s);
798 dst = std::copy(v.begin(), v.end(), dst);
801 template <
typename String = sstring,
typename... Args>
802 static String make_sstring(Args&&... args)
804 String ret = uninitialized_string<String>((str_len(args) + ...));
805 auto dst = ret.data();
806 (copy_str_to(dst, args), ...);
811 template <
typename string_type,
typename T>
812 string_type to_sstring(T value) {
813 auto size = fmt::formatted_size(
"{}", value);
814 auto formatted = uninitialized_string<string_type>(size);
815 fmt::format_to(formatted.data(),
"{}", value);
819 template <
typename string_type>
820 string_type to_sstring(
const char* value) {
821 return string_type(value);
824 template <
typename string_type>
825 string_type to_sstring(sstring value) {
829 template <
typename string_type>
830 string_type to_sstring(
const temporary_buffer<char>& buf) {
831 return string_type(buf.get(), buf.size());
835 template <
typename string_type = s
string,
typename T>
836 string_type to_sstring(T value) {
837 return internal::to_sstring<string_type>(value);
843 SEASTAR_MODULE_EXPORT
844 template <
typename T>
846 std::ostream& operator<<(std::ostream& os,
const std::vector<T>& v) {
849 for (
auto&& elem : v) {
861 SEASTAR_MODULE_EXPORT
862 template <
typename Key,
typename T,
typename Hash,
typename KeyEqual,
typename Allocator>
863 std::ostream& operator<<(std::ostream& os,
const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v) {
866 for (
auto&& elem : v) {
872 os <<
"{" << elem.first <<
" -> " << elem.second <<
"}";
879 #if FMT_VERSION >= 90000
884 SEASTAR_MODULE_EXPORT
885 template <
typename char_type,
typename Size, Size max_size,
bool NulTerminate>
886 struct fmt::formatter<
seastar::basic_sstring<char_type, Size, max_size, NulTerminate>>
887 :
public fmt::formatter<std::basic_string_view<char_type>> {
888 using format_as_t = std::basic_string_view<char_type>;
889 using base = fmt::formatter<format_as_t>;
890 template <
typename FormatContext>
891 auto format(const ::seastar::basic_sstring<char_type, Size, max_size, NulTerminate>& s, FormatContext& ctx)
const {
892 return base::format(format_as_t{s}, ctx);
Definition: sstring.hh:75
reference back() noexcept
Definition: sstring.hh:493
const_reference back() const noexcept
Definition: sstring.hh:503
const_reference front() const noexcept
Definition: sstring.hh:482
void resize_and_overwrite(size_t n, Operation op)
Definition: sstring.hh:358
void insert(const_iterator p, InputIterator beg, InputIterator end)
Definition: sstring.hh:460
void resize(size_t n, const char_type c='\0')
Definition: sstring.hh:372
reference front() noexcept
Definition: sstring.hh:471
size_t find_last_of(char_type c, size_t pos=npos) const noexcept
Definition: sstring.hh:320
basic_sstring & replace(size_type pos, size_type n1, const char_type *s, size_type n2)
Definition: sstring.hh:396
basic_sstring & append(const char_type *s, size_t n)
Definition: sstring.hh:343
Definition: sstring.hh:123
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
sstring format(const char *fmt, A &&... a)
Definition: print.hh:142
Definition: sstring.hh:82
Definition: sstring.hh:87