Seastar
High performance C++ framework for concurrent servers
sstring.hh
1/*
2 * This file is open source software, licensed to you under the terms
3 * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
4 * distributed with this work for additional information regarding copyright
5 * ownership. You may not use this file except in compliance with the License.
6 *
7 * You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing,
12 * software distributed under the License is distributed on an
13 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14 * KIND, either express or implied. See the License for the
15 * specific language governing permissions and limitations
16 * under the License.
17 */
18/*
19 * Copyright 2014 Cloudius Systems
20 */
21
22#pragma once
23
24#ifndef SEASTAR_MODULE
25#include <stdint.h>
26#include <algorithm>
27#include <cassert>
28#if __has_include(<compare>)
29#include <compare>
30#endif
31#include <string>
32#include <vector>
33#include <unordered_map>
34#include <cstring>
35#include <initializer_list>
36#include <istream>
37#include <ostream>
38#include <functional>
39#include <type_traits>
40#include <fmt/format.h>
41#if FMT_VERSION >= 110000
42#include <fmt/ranges.h>
43#endif
44#endif
45#include <seastar/util/std-compat.hh>
46#include <seastar/util/modules.hh>
47#include <seastar/core/temporary_buffer.hh>
48
49namespace seastar {
50
51SEASTAR_MODULE_EXPORT_BEGIN
52
53template <typename char_type, typename Size, Size max_size, bool NulTerminate = true>
54class basic_sstring;
55
56#ifdef SEASTAR_SSTRING
57// Older std::string used atomic reference counting and had no small-buffer-optimization.
58// At some point the new std::string ABI improved -- no reference counting plus the small
59// buffer optimization. However, aliasing seastar::sstring to std::string still ends up
60// with a small performance degradation. (FIXME?)
61using sstring = basic_sstring<char, uint32_t, 15>;
62#else
63using sstring = std::string;
64#endif
65
66SEASTAR_MODULE_EXPORT_END
67
68namespace internal {
69[[noreturn]] void throw_bad_alloc();
70[[noreturn]] void throw_sstring_overflow();
71[[noreturn]] void throw_sstring_out_of_range();
72}
73
74SEASTAR_MODULE_EXPORT
75template <typename char_type, typename Size, Size max_size, bool NulTerminate>
77 static_assert(
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");
82 union contents {
84 char_type* str;
85 Size size;
86 int8_t pad;
87 } external;
89 char_type str[max_size];
90 int8_t size;
91 } internal;
92 static_assert(sizeof(external_type) <= sizeof(internal_type), "max_size too small");
93 static_assert(max_size <= 127, "max_size too large");
94 } u;
95 bool is_internal() const noexcept {
96 return u.internal.size >= 0;
97 }
98 bool is_external() const noexcept {
99 return !is_internal();
100 }
101 const char_type* str() const noexcept {
102 return is_internal() ? u.internal.str : u.external.str;
103 }
104 char_type* str() noexcept {
105 return is_internal() ? u.internal.str : u.external.str;
106 }
107
108public:
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*;
118 // FIXME: add reverse_iterator and friend
119 using difference_type = ssize_t; // std::make_signed_t<Size> can be too small
120 using size_type = Size;
121 static constexpr size_type npos = static_cast<size_type>(-1);
122 static constexpr unsigned padding() { return unsigned(NulTerminate); }
123public:
125
126 basic_sstring() noexcept {
127 u.internal.size = 0;
128 if (NulTerminate) {
129 u.internal.str[0] = '\0';
130 }
131 }
132 basic_sstring(const basic_sstring& x) {
133 if (x.is_internal()) {
134 u.internal = x.u.internal;
135 } else {
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();
140 }
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;
143 }
144 }
145 basic_sstring(basic_sstring&& x) noexcept {
146#pragma GCC diagnostic push
147 // Is a small-string construction is followed by this move constructor, then the trailing bytes
148 // of x.u are not initialized, but copied. gcc complains, but it is both legitimate to copy
149 // these bytes, and more efficient than a variable-size copy
150#pragma GCC diagnostic ignored "-Wuninitialized"
151 u = x.u;
152#pragma GCC diagnostic pop
153 x.u.internal.size = 0;
154 x.u.internal.str[0] = '\0';
155 }
156 basic_sstring(initialized_later, size_t size) {
157 if (size_type(size) != size) {
158 internal::throw_sstring_overflow();
159 }
160 if (size + padding() <= sizeof(u.internal.str)) {
161 if (NulTerminate) {
162 u.internal.str[size] = '\0';
163 }
164 u.internal.size = size;
165 } else {
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();
170 }
171 u.external.size = size;
172 if (NulTerminate) {
173 u.external.str[size] = '\0';
174 }
175 }
176 }
177 basic_sstring(const char_type* x, size_t size) {
178 if (size_type(size) != size) {
179 internal::throw_sstring_overflow();
180 }
181 if (size + padding() <= sizeof(u.internal.str)) {
182 std::copy(x, x + size, u.internal.str);
183 if (NulTerminate) {
184 u.internal.str[size] = '\0';
185 }
186 u.internal.size = size;
187 } else {
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();
192 }
193 u.external.size = size;
194 std::copy(x, x + size, u.external.str);
195 if (NulTerminate) {
196 u.external.str[size] = '\0';
197 }
198 }
199 }
200
201 basic_sstring(size_t size, char_type x) : basic_sstring(initialized_later(), size) {
202 memset(begin(), x, size);
203 }
204
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());
215 }
216 explicit basic_sstring(std::basic_string_view<char_type, traits_type> v)
217 : basic_sstring(v.data(), v.size()) {
218 }
219 ~basic_sstring() noexcept {
220 if (is_external()) {
221 std::free(u.external.str);
222 }
223 }
224 basic_sstring& operator=(const basic_sstring& x) {
225 basic_sstring tmp(x);
226 swap(tmp);
227 return *this;
228 }
229 basic_sstring& operator=(basic_sstring&& x) noexcept {
230 if (this != &x) {
231 this->~basic_sstring();
232 new (this) basic_sstring(std::move(x));
233 }
234 return *this;
235 }
236 operator std::basic_string<char_type>() const {
237 return { str(), size() };
238 }
239
240 size_t size() const noexcept {
241 return is_internal() ? u.internal.size : u.external.size;
242 }
243
244 size_t length() const noexcept {
245 return size();
246 }
247
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();
251 while (it < end) {
252 if (*it == t) {
253 return it - str();
254 }
255 it++;
256 }
257 return npos;
258 }
259
260 size_t find(const char_type* c_str, size_t pos, size_t len2) const noexcept {
261 assert(c_str != nullptr || len2 == 0);
262 if (pos > size()) {
263 return npos;
264 }
265
266 if (len2 == 0) {
267 return pos;
268 }
269
270 const char_type* it = str() + pos;
271 const char_type* end = str() + size();
272 size_t len1 = end - it;
273 if (len1 < len2) {
274 return npos;
275 }
276
277 char_type f2 = *c_str;
278 while (true) {
279 len1 = end - it;
280 if (len1 < len2) {
281 return npos;
282 }
283
284 // find the first byte of pattern string matching in source string
285 it = traits_type::find(it, len1 - len2 + 1, f2);
286 if (it == nullptr) {
287 return npos;
288 }
289
290 if (traits_type::compare(it, c_str, len2) == 0) {
291 return it - str();
292 }
293
294 ++it;
295 }
296 }
297
298 constexpr size_t find(const char_type* s, size_t pos = 0) const noexcept {
299 return find(s, pos, traits_type::length(s));
300 }
301
302 size_t find(const basic_sstring& s, size_t pos = 0) const noexcept {
303 return find(s.str(), pos, s.size());
304 }
305
306 template<class StringViewLike,
307 std::enable_if_t<std::is_convertible_v<StringViewLike,
308 std::basic_string_view<char_type, traits_type>>,
309 int> = 0>
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());
313 }
314
321 size_t find_last_of (char_type c, size_t pos = npos) const noexcept {
322 const char_type* str_start = str();
323 if (size()) {
324 if (pos >= size()) {
325 pos = size() - 1;
326 }
327 const char_type* p = str_start + pos + 1;
328 do {
329 p--;
330 if (*p == c) {
331 return (p - str_start);
332 }
333 } while (p != str_start);
334 }
335 return npos;
336 }
337
344 basic_sstring& append (const char_type* s, size_t n) {
345 basic_sstring ret(initialized_later(), size() + n);
346 std::copy(begin(), end(), ret.begin());
347 std::copy(s, s + n, ret.begin() + size());
348 *this = std::move(ret);
349 return *this;
350 }
351
357 template <class Operation>
358 requires std::is_invocable_r_v<size_t, Operation, char_type*, size_t>
359 void resize_and_overwrite(size_t n, Operation op) {
360 if (n > size()) {
361 *this = basic_sstring(initialized_later(), n);
362 }
363 size_t r = std::move(op)(data(), n);
364 assert(r <= n);
365 resize(r);
366 }
367
373 void resize(size_t n, const char_type c = '\0') {
374 if (n > size()) {
375 *this += basic_sstring(n - size(), c);
376 } else if (n < size()) {
377 if (is_internal()) {
378 u.internal.size = n;
379 if (NulTerminate) {
380 u.internal.str[n] = '\0';
381 }
382 } else if (n + padding() <= sizeof(u.internal.str)) {
383 *this = basic_sstring(u.external.str, n);
384 } else {
385 u.external.size = n;
386 if (NulTerminate) {
387 u.external.str[n] = '\0';
388 }
389 }
390 }
391 }
392
397 basic_sstring& replace(size_type pos, size_type n1, const char_type* s,
398 size_type n2) {
399 if (pos > size()) {
400 internal::throw_sstring_out_of_range();
401 }
402
403 if (n1 > size() - pos) {
404 n1 = size() - pos;
405 }
406
407 if (n1 == n2) {
408 if (n2) {
409 std::copy(s, s + n2, begin() + pos);
410 }
411 return *this;
412 }
413 basic_sstring ret(initialized_later(), size() + n2 - n1);
414 char_type* p= ret.begin();
415 std::copy(begin(), begin() + pos, p);
416 p += pos;
417 if (n2) {
418 std::copy(s, s + n2, p);
419 }
420 p += n2;
421 std::copy(begin() + pos + n1, end(), p);
422 *this = std::move(ret);
423 return *this;
424 }
425
426 template <class InputIterator>
427 basic_sstring& replace (const_iterator i1, const_iterator i2,
428 InputIterator first, InputIterator last) {
429 if (i1 < begin() || i1 > end() || i2 < begin()) {
430 internal::throw_sstring_out_of_range();
431 }
432 if (i2 > end()) {
433 i2 = end();
434 }
435
436 if (i2 - i1 == last - first) {
437 //in place replacement
438 std::copy(first, last, const_cast<char_type*>(i1));
439 return *this;
440 }
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);
447 return *this;
448 }
449
450 iterator erase(iterator first, iterator last) {
451 size_t pos = first - begin();
452 replace(pos, last - first, nullptr, 0);
453 return begin() + pos;
454 }
455
460 template <class InputIterator>
461 void insert(const_iterator p, InputIterator beg, InputIterator end) {
462 replace(p, p, beg, end);
463 }
464
465
471 reference
472 front() noexcept {
473 assert(!empty());
474 return *str();
475 }
476
482 const_reference
483 front() const noexcept {
484 assert(!empty());
485 return *str();
486 }
487
493 reference
494 back() noexcept {
495 return operator[](size() - 1);
496 }
497
503 const_reference
504 back() const noexcept {
505 return operator[](size() - 1);
506 }
507
508 basic_sstring substr(size_t from, size_t len = npos) const {
509 if (from > size()) {
510 internal::throw_sstring_out_of_range();
511 }
512 if (len > size() - from) {
513 len = size() - from;
514 }
515 if (len == 0) {
516 return "";
517 }
518 return { str() + from , len };
519 }
520
521 const char_type& at(size_t pos) const {
522 if (pos >= size()) {
523 internal::throw_sstring_out_of_range();
524 }
525 return *(str() + pos);
526 }
527
528 char_type& at(size_t pos) {
529 if (pos >= size()) {
530 internal::throw_sstring_out_of_range();
531 }
532 return *(str() + pos);
533 }
534
535 bool empty() const noexcept {
536 return u.internal.size == 0;
537 }
538
539 // Deprecated March 2020.
540 [[deprecated("Use = {}")]]
541 void reset() noexcept {
542 if (is_external()) {
543 std::free(u.external.str);
544 }
545 u.internal.size = 0;
546 if (NulTerminate) {
547 u.internal.str[0] = '\0';
548 }
549 }
550 temporary_buffer<char_type> release() && {
551 if (is_external()) {
552 auto ptr = u.external.str;
553 auto size = u.external.size;
554 u.external.str = nullptr;
555 u.external.size = 0;
556 return temporary_buffer<char_type>(ptr, size, make_free_deleter(ptr));
557 } else {
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());
560 u.internal.size = 0;
561 if (NulTerminate) {
562 u.internal.str[0] = '\0';
563 }
564 return buf;
565 }
566 }
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()));
569 if (n != 0) {
570 return n;
571 }
572 if (size() < x.size()) {
573 return -1;
574 } else if (size() > x.size()) {
575 return 1;
576 } else {
577 return 0;
578 }
579 }
580
581 int compare(size_t pos, size_t sz, std::basic_string_view<char_type, traits_type> x) const {
582 if (pos > size()) {
583 internal::throw_sstring_out_of_range();
584 }
585
586 sz = std::min(size() - pos, sz);
587 auto n = traits_type::compare(begin() + pos, x.begin(), std::min(sz, x.size()));
588 if (n != 0) {
589 return n;
590 }
591 if (sz < x.size()) {
592 return -1;
593 } else if (sz > x.size()) {
594 return 1;
595 } else {
596 return 0;
597 }
598 }
599
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;
602 }
603
604 constexpr bool starts_with(char_type c) const noexcept {
605 return !empty() && traits_type::eq(front(), c);
606 }
607
608 constexpr bool starts_with(const char_type* s) const noexcept {
609 return starts_with(std::basic_string_view<char_type, traits_type>(s));
610 }
611
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;
614 }
615
616 constexpr bool ends_with(char_type c) const noexcept {
617 return !empty() && traits_type::eq(back(), c);
618 }
619
620 constexpr bool ends_with(const char_type* s) const noexcept {
621 return ends_with(std::basic_string_view<char_type, traits_type>(s));
622 }
623
624 constexpr bool contains(std::basic_string_view<char_type, traits_type> sv) const noexcept {
625 return find(sv) != npos;
626 }
627
628 constexpr bool contains(char_type c) const noexcept {
629 return find(c) != npos;
630 }
631
632 constexpr bool contains(const char_type* s) const noexcept {
633 return find(s) != npos;
634 }
635
636 void swap(basic_sstring& x) noexcept {
637 contents tmp;
638 tmp = x.u;
639 x.u = u;
640 u = tmp;
641 }
642 char_type* data() noexcept {
643 return str();
644 }
645 const char_type* data() const noexcept {
646 return str();
647 }
648 const char_type* c_str() const noexcept {
649 return str();
650 }
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());
659 }
660 bool operator!=(const basic_sstring& x) const noexcept {
661 return !operator==(x);
662 }
663 constexpr bool operator==(const std::basic_string<char_type> x) const noexcept {
664 return compare(x) == 0;
665 }
666 constexpr bool operator==(const char_type* x) const noexcept {
667 return compare(x) == 0;
668 }
669#if __cpp_lib_three_way_comparison
670 constexpr std::strong_ordering operator<=>(const auto& x) const noexcept {
671 return compare(x) <=> 0;
672 }
673#else
674 bool operator<(const basic_sstring& x) const noexcept {
675 return compare(x) < 0;
676 }
677#endif
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());
682 return ret;
683 }
684 basic_sstring& operator+=(const basic_sstring& x) {
685 return *this = *this + x;
686 }
687 char_type& operator[](size_type pos) noexcept {
688 return str()[pos];
689 }
690 const char_type& operator[](size_type pos) const noexcept {
691 return str()[pos];
692 }
693
694 operator std::basic_string_view<char_type>() const noexcept {
695 // we assume that std::basic_string_view<char_type>(str(), size())
696 // won't throw, although it is not specified as noexcept in
697 // https://en.cppreference.com/w/cpp/string/basic_string_view/basic_string_view
698 // at this time (C++20).
699 //
700 // This is similar to std::string operator std::basic_string_view:
701 // https://en.cppreference.com/w/cpp/string/basic_string/operator_basic_string_view
702 // that is specified as noexcept too.
703 static_assert(noexcept(std::basic_string_view<char_type>(str(), size())));
704 return std::basic_string_view<char_type>(str(), size());
705 }
706};
707
708namespace internal {
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 {};
712}
713
714SEASTAR_MODULE_EXPORT
715template <typename string_type = sstring>
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);
719 } else {
720 string_type ret;
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; });
723#else
724 ret.resize(size);
725#endif
726 return ret;
727 }
728}
729
730SEASTAR_MODULE_EXPORT
731template <typename char_type, typename size_type, size_type Max, size_type N, bool NulTerminate>
732inline
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>;
736 // don't copy the terminating NUL character
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);
740 return ret;
741}
742
743template <typename T>
744static inline
745size_t constexpr str_len(const T& s) {
746 return std::string_view(s).size();
747}
748
749SEASTAR_MODULE_EXPORT
750template <typename char_type, typename size_type, size_type max_size>
751inline
752void swap(basic_sstring<char_type, size_type, max_size>& x,
753 basic_sstring<char_type, size_type, max_size>& y) noexcept
754{
755 return x.swap(y);
756}
757
758SEASTAR_MODULE_EXPORT
759template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, typename char_traits>
760inline
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());
765}
766
767SEASTAR_MODULE_EXPORT
768template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, typename char_traits>
769inline
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) {
773 std::string tmp;
774 is >> tmp;
775 s = tmp;
776 return is;
777}
778
779}
780
781namespace std {
782
783SEASTAR_MODULE_EXPORT
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);
788 }
789};
790
791}
792
793namespace seastar {
794
795template <typename T>
796static inline
797void copy_str_to(char*& dst, const T& s) {
798 std::string_view v(s);
799 dst = std::copy(v.begin(), v.end(), dst);
800}
801
802template <typename String = sstring, typename... Args>
803static String make_sstring(Args&&... args)
804{
805 String ret = uninitialized_string<String>((str_len(args) + ...));
806 auto dst = ret.data();
807 (copy_str_to(dst, args), ...);
808 return ret;
809}
810
811namespace internal {
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);
817 return formatted;
818}
819
820template <typename string_type>
821string_type to_sstring(const char* value) {
822 return string_type(value);
823}
824
825template <typename string_type>
826string_type to_sstring(sstring value) {
827 return value;
828}
829
830template <typename string_type>
831string_type to_sstring(const temporary_buffer<char>& buf) {
832 return string_type(buf.get(), buf.size());
833}
834}
835
836template <typename string_type = sstring, typename T>
837string_type to_sstring(T value) {
838 return internal::to_sstring<string_type>(value);
839}
840}
841
842#ifdef SEASTAR_DEPRECATED_OSTREAM_FORMATTERS
843
844namespace std {
845
846SEASTAR_MODULE_EXPORT
847template <typename T>
848[[deprecated("Use {fmt} instead")]]
849inline
850std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
851 bool first = true;
852 os << "{";
853 for (auto&& elem : v) {
854 if (!first) {
855 os << ", ";
856 } else {
857 first = false;
858 }
859 os << elem;
860 }
861 os << "}";
862 return os;
863}
864
865SEASTAR_MODULE_EXPORT
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) {
869 bool first = true;
870 os << "{";
871 for (auto&& elem : v) {
872 if (!first) {
873 os << ", ";
874 } else {
875 first = false;
876 }
877 os << "{" << elem.first << " -> " << elem.second << "}";
878 }
879 os << "}";
880 return os;
881}
882}
883
884#endif
885
886#if FMT_VERSION >= 110000
887
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>
890{};
891
892#endif
893
894#if FMT_VERSION >= 90000
895
896// Due to https://github.com/llvm/llvm-project/issues/68849, we inherit
897// from formatter<string_view> publicly rather than privately
898
899SEASTAR_MODULE_EXPORT
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);
908 }
909};
910
911#endif
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
Seastar API namespace.
Definition: abort_on_ebadf.hh:26
sstring format(fmt::format_string< A... > fmt, A &&... a)
Definition: print.hh:132
STL namespace.
ostream & operator<<(ostream &os, const seastar::lazy_eval< Func > &lf)
Definition: lazy.hh:131