]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/core/sstring.hh
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / seastar / include / seastar / core / sstring.hh
CommitLineData
11fdf7f2
TL
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#include <stdint.h>
25#include <algorithm>
1e59de90 26#include <cassert>
11fdf7f2
TL
27#include <string>
28#include <vector>
29#include <unordered_map>
30#include <cstring>
31#include <stdexcept>
32#include <initializer_list>
33#include <istream>
34#include <ostream>
35#include <functional>
11fdf7f2 36#include <type_traits>
1e59de90
TL
37#include <fmt/ostream.h>
38#include <seastar/util/concepts.hh>
11fdf7f2
TL
39#include <seastar/util/std-compat.hh>
40#include <seastar/core/temporary_buffer.hh>
41
42namespace seastar {
43
44template <typename char_type, typename Size, Size max_size, bool NulTerminate = true>
45class basic_sstring;
46
f67539c2 47#ifdef SEASTAR_SSTRING
1e59de90
TL
48// Older std::string used atomic reference counting and had no small-buffer-optimization.
49// At some point the new std::string ABI improved -- no reference counting plus the small
50// buffer optimization. However, aliasing seastar::sstring to std::string still ends up
51// with a small performance degradation. (FIXME?)
11fdf7f2 52using sstring = basic_sstring<char, uint32_t, 15>;
f67539c2
TL
53#else
54using sstring = std::string;
55#endif
56
57namespace internal {
58[[noreturn]] void throw_bad_alloc();
59[[noreturn]] void throw_sstring_overflow();
60[[noreturn]] void throw_sstring_out_of_range();
61}
11fdf7f2
TL
62
63template <typename char_type, typename Size, Size max_size, bool NulTerminate>
64class basic_sstring {
65 static_assert(
66 (std::is_same<char_type, char>::value
67 || std::is_same<char_type, signed char>::value
68 || std::is_same<char_type, unsigned char>::value),
69 "basic_sstring only supports single byte char types");
70 union contents {
71 struct external_type {
72 char_type* str;
73 Size size;
74 int8_t pad;
75 } external;
76 struct internal_type {
77 char_type str[max_size];
78 int8_t size;
79 } internal;
80 static_assert(sizeof(external_type) <= sizeof(internal_type), "max_size too small");
81 static_assert(max_size <= 127, "max_size too large");
82 } u;
83 bool is_internal() const noexcept {
84 return u.internal.size >= 0;
85 }
86 bool is_external() const noexcept {
87 return !is_internal();
88 }
f67539c2 89 const char_type* str() const noexcept {
11fdf7f2
TL
90 return is_internal() ? u.internal.str : u.external.str;
91 }
f67539c2 92 char_type* str() noexcept {
11fdf7f2
TL
93 return is_internal() ? u.internal.str : u.external.str;
94 }
95
11fdf7f2
TL
96public:
97 using value_type = char_type;
98 using traits_type = std::char_traits<char_type>;
99 using allocator_type = std::allocator<char_type>;
100 using reference = char_type&;
101 using const_reference = const char_type&;
102 using pointer = char_type*;
103 using const_pointer = const char_type*;
104 using iterator = char_type*;
105 using const_iterator = const char_type*;
106 // FIXME: add reverse_iterator and friend
107 using difference_type = ssize_t; // std::make_signed_t<Size> can be too small
108 using size_type = Size;
109 static constexpr size_type npos = static_cast<size_type>(-1);
110 static constexpr unsigned padding() { return unsigned(NulTerminate); }
111public:
112 struct initialized_later {};
113
114 basic_sstring() noexcept {
115 u.internal.size = 0;
116 if (NulTerminate) {
117 u.internal.str[0] = '\0';
118 }
119 }
120 basic_sstring(const basic_sstring& x) {
121 if (x.is_internal()) {
122 u.internal = x.u.internal;
123 } else {
124 u.internal.size = -1;
125 u.external.str = reinterpret_cast<char_type*>(std::malloc(x.u.external.size + padding()));
126 if (!u.external.str) {
f67539c2 127 internal::throw_bad_alloc();
11fdf7f2
TL
128 }
129 std::copy(x.u.external.str, x.u.external.str + x.u.external.size + padding(), u.external.str);
130 u.external.size = x.u.external.size;
131 }
132 }
133 basic_sstring(basic_sstring&& x) noexcept {
9f95a23c
TL
134#pragma GCC diagnostic push
135 // Is a small-string construction is followed by this move constructor, then the trailing bytes
136 // of x.u are not initialized, but copied. gcc complains, but it is both legitimate to copy
137 // these bytes, and more efficient than a variable-size copy
138#pragma GCC diagnostic ignored "-Wuninitialized"
11fdf7f2 139 u = x.u;
9f95a23c 140#pragma GCC diagnostic pop
11fdf7f2
TL
141 x.u.internal.size = 0;
142 x.u.internal.str[0] = '\0';
143 }
144 basic_sstring(initialized_later, size_t size) {
145 if (size_type(size) != size) {
f67539c2 146 internal::throw_sstring_overflow();
11fdf7f2
TL
147 }
148 if (size + padding() <= sizeof(u.internal.str)) {
149 if (NulTerminate) {
150 u.internal.str[size] = '\0';
151 }
152 u.internal.size = size;
153 } else {
154 u.internal.size = -1;
155 u.external.str = reinterpret_cast<char_type*>(std::malloc(size + padding()));
156 if (!u.external.str) {
f67539c2 157 internal::throw_bad_alloc();
11fdf7f2
TL
158 }
159 u.external.size = size;
160 if (NulTerminate) {
161 u.external.str[size] = '\0';
162 }
163 }
164 }
165 basic_sstring(const char_type* x, size_t size) {
166 if (size_type(size) != size) {
f67539c2 167 internal::throw_sstring_overflow();
11fdf7f2
TL
168 }
169 if (size + padding() <= sizeof(u.internal.str)) {
170 std::copy(x, x + size, u.internal.str);
171 if (NulTerminate) {
172 u.internal.str[size] = '\0';
173 }
174 u.internal.size = size;
175 } else {
176 u.internal.size = -1;
177 u.external.str = reinterpret_cast<char_type*>(std::malloc(size + padding()));
178 if (!u.external.str) {
f67539c2 179 internal::throw_bad_alloc();
11fdf7f2
TL
180 }
181 u.external.size = size;
182 std::copy(x, x + size, u.external.str);
183 if (NulTerminate) {
184 u.external.str[size] = '\0';
185 }
186 }
187 }
188
189 basic_sstring(size_t size, char_type x) : basic_sstring(initialized_later(), size) {
190 memset(begin(), x, size);
191 }
192
193 basic_sstring(const char* x) : basic_sstring(reinterpret_cast<const char_type*>(x), std::strlen(x)) {}
194 basic_sstring(std::basic_string<char_type>& x) : basic_sstring(x.c_str(), x.size()) {}
195 basic_sstring(std::initializer_list<char_type> x) : basic_sstring(x.begin(), x.end() - x.begin()) {}
196 basic_sstring(const char_type* b, const char_type* e) : basic_sstring(b, e - b) {}
197 basic_sstring(const std::basic_string<char_type>& s)
198 : basic_sstring(s.data(), s.size()) {}
199 template <typename InputIterator>
200 basic_sstring(InputIterator first, InputIterator last)
201 : basic_sstring(initialized_later(), std::distance(first, last)) {
202 std::copy(first, last, begin());
203 }
f67539c2 204 explicit basic_sstring(std::basic_string_view<char_type, traits_type> v)
11fdf7f2
TL
205 : basic_sstring(v.data(), v.size()) {
206 }
207 ~basic_sstring() noexcept {
208 if (is_external()) {
209 std::free(u.external.str);
210 }
211 }
212 basic_sstring& operator=(const basic_sstring& x) {
213 basic_sstring tmp(x);
214 swap(tmp);
215 return *this;
216 }
217 basic_sstring& operator=(basic_sstring&& x) noexcept {
218 if (this != &x) {
f67539c2
TL
219 this->~basic_sstring();
220 new (this) basic_sstring(std::move(x));
11fdf7f2
TL
221 }
222 return *this;
223 }
224 operator std::basic_string<char_type>() const {
225 return { str(), size() };
226 }
9f95a23c 227
11fdf7f2
TL
228 size_t size() const noexcept {
229 return is_internal() ? u.internal.size : u.external.size;
230 }
231
232 size_t length() const noexcept {
233 return size();
234 }
235
236 size_t find(char_type t, size_t pos = 0) const noexcept {
237 const char_type* it = str() + pos;
238 const char_type* end = str() + size();
239 while (it < end) {
240 if (*it == t) {
241 return it - str();
242 }
243 it++;
244 }
245 return npos;
246 }
247
248 size_t find(const basic_sstring& s, size_t pos = 0) const noexcept {
249 const char_type* it = str() + pos;
250 const char_type* end = str() + size();
251 const char_type* c_str = s.str();
11fdf7f2 252
1e59de90
TL
253 if (pos > size()) {
254 return npos;
255 }
256
257 const size_t len2 = s.size();
258 if (len2 == 0) {
259 return pos;
260 }
261
262 size_t len1 = end - it;
263 if (len1 < len2) {
264 return npos;
265 }
266
267 char_type f2 = *c_str;
268 while (true) {
269 len1 = end - it;
270 if (len1 < len2) {
271 return npos;
272 }
273
274 // find the first byte of pattern string matching in source string
275 it = traits_type::find(it, len1 - len2 + 1, f2);
276 if (it == nullptr) {
277 return npos;
11fdf7f2 278 }
1e59de90
TL
279
280 if (traits_type::compare(it, c_str, len2) == 0) {
11fdf7f2
TL
281 return it - str();
282 }
1e59de90
TL
283
284 ++it;
11fdf7f2 285 }
11fdf7f2
TL
286 }
287
288 /**
289 * find_last_of find the last occurrence of c in the string.
290 * When pos is specified, the search only includes characters
291 * at or before position pos.
292 *
293 */
294 size_t find_last_of (char_type c, size_t pos = npos) const noexcept {
295 const char_type* str_start = str();
296 if (size()) {
297 if (pos >= size()) {
298 pos = size() - 1;
299 }
300 const char_type* p = str_start + pos + 1;
301 do {
302 p--;
303 if (*p == c) {
304 return (p - str_start);
305 }
306 } while (p != str_start);
307 }
308 return npos;
309 }
310
311 /**
312 * Append a C substring.
313 * @param s The C string to append.
314 * @param n The number of characters to append.
315 * @return Reference to this string.
316 */
317 basic_sstring& append (const char_type* s, size_t n) {
318 basic_sstring ret(initialized_later(), size() + n);
319 std::copy(begin(), end(), ret.begin());
320 std::copy(s, s + n, ret.begin() + size());
321 *this = std::move(ret);
322 return *this;
323 }
324
1e59de90
TL
325 /**
326 * Resize string and use the specified @c op to modify the content and the length
327 * @param n new size
328 * @param op the function object used for setting the new content of the string
329 */
330 template <class Operation>
331 SEASTAR_CONCEPT( requires std::is_invocable_r<size_t, Operation, char_type*, size_t>::value )
332 void resize_and_overwrite(size_t n, Operation op) {
333 if (n > size()) {
334 *this = basic_sstring(initialized_later(), n);
335 }
336 size_t r = std::move(op)(data(), n);
337 assert(r <= n);
338 resize(r);
339 }
340
11fdf7f2
TL
341 /**
342 * Resize string.
343 * @param n new size.
344 * @param c if n greater than current size character to fill newly allocated space with.
345 */
346 void resize(size_t n, const char_type c = '\0') {
347 if (n > size()) {
348 *this += basic_sstring(n - size(), c);
349 } else if (n < size()) {
350 if (is_internal()) {
351 u.internal.size = n;
9f95a23c
TL
352 if (NulTerminate) {
353 u.internal.str[n] = '\0';
354 }
11fdf7f2
TL
355 } else if (n + padding() <= sizeof(u.internal.str)) {
356 *this = basic_sstring(u.external.str, n);
357 } else {
358 u.external.size = n;
9f95a23c
TL
359 if (NulTerminate) {
360 u.external.str[n] = '\0';
361 }
11fdf7f2
TL
362 }
363 }
364 }
365
366 /**
367 * Replace characters with a value of a C style substring.
368 *
369 */
370 basic_sstring& replace(size_type pos, size_type n1, const char_type* s,
371 size_type n2) {
372 if (pos > size()) {
f67539c2 373 internal::throw_sstring_out_of_range();
11fdf7f2
TL
374 }
375
376 if (n1 > size() - pos) {
377 n1 = size() - pos;
378 }
379
380 if (n1 == n2) {
381 if (n2) {
382 std::copy(s, s + n2, begin() + pos);
383 }
384 return *this;
385 }
386 basic_sstring ret(initialized_later(), size() + n2 - n1);
387 char_type* p= ret.begin();
388 std::copy(begin(), begin() + pos, p);
389 p += pos;
390 if (n2) {
391 std::copy(s, s + n2, p);
392 }
393 p += n2;
394 std::copy(begin() + pos + n1, end(), p);
395 *this = std::move(ret);
396 return *this;
397 }
398
399 template <class InputIterator>
400 basic_sstring& replace (const_iterator i1, const_iterator i2,
401 InputIterator first, InputIterator last) {
402 if (i1 < begin() || i1 > end() || i2 < begin()) {
f67539c2 403 internal::throw_sstring_out_of_range();
11fdf7f2
TL
404 }
405 if (i2 > end()) {
406 i2 = end();
407 }
408
409 if (i2 - i1 == last - first) {
410 //in place replacement
411 std::copy(first, last, const_cast<char_type*>(i1));
412 return *this;
413 }
414 basic_sstring ret(initialized_later(), size() + (last - first) - (i2 - i1));
415 char_type* p = ret.begin();
416 p = std::copy(cbegin(), i1, p);
417 p = std::copy(first, last, p);
418 std::copy(i2, cend(), p);
419 *this = std::move(ret);
420 return *this;
421 }
422
423 iterator erase(iterator first, iterator last) {
424 size_t pos = first - begin();
425 replace(pos, last - first, nullptr, 0);
426 return begin() + pos;
427 }
428
429 /**
430 * Inserts additional characters into the string right before
431 * the character indicated by p.
432 */
433 template <class InputIterator>
434 void insert(const_iterator p, InputIterator beg, InputIterator end) {
435 replace(p, p, beg, end);
436 }
437
438 /**
439 * Returns a read/write reference to the data at the last
440 * element of the string.
441 * This function shall not be called on empty strings.
442 */
443 reference
444 back() noexcept {
445 return operator[](size() - 1);
446 }
447
448 /**
449 * Returns a read-only (constant) reference to the data at the last
450 * element of the string.
451 * This function shall not be called on empty strings.
452 */
453 const_reference
454 back() const noexcept {
455 return operator[](size() - 1);
456 }
457
458 basic_sstring substr(size_t from, size_t len = npos) const {
459 if (from > size()) {
f67539c2 460 internal::throw_sstring_out_of_range();
11fdf7f2
TL
461 }
462 if (len > size() - from) {
463 len = size() - from;
464 }
465 if (len == 0) {
466 return "";
467 }
468 return { str() + from , len };
469 }
470
471 const char_type& at(size_t pos) const {
472 if (pos >= size()) {
f67539c2 473 internal::throw_sstring_out_of_range();
11fdf7f2
TL
474 }
475 return *(str() + pos);
476 }
477
478 char_type& at(size_t pos) {
479 if (pos >= size()) {
f67539c2 480 internal::throw_sstring_out_of_range();
11fdf7f2
TL
481 }
482 return *(str() + pos);
483 }
484
485 bool empty() const noexcept {
486 return u.internal.size == 0;
487 }
f67539c2
TL
488
489 // Deprecated March 2020.
490 [[deprecated("Use = {}")]]
11fdf7f2
TL
491 void reset() noexcept {
492 if (is_external()) {
493 std::free(u.external.str);
494 }
495 u.internal.size = 0;
496 if (NulTerminate) {
497 u.internal.str[0] = '\0';
498 }
499 }
500 temporary_buffer<char_type> release() && {
501 if (is_external()) {
502 auto ptr = u.external.str;
503 auto size = u.external.size;
504 u.external.str = nullptr;
505 u.external.size = 0;
506 return temporary_buffer<char_type>(ptr, size, make_free_deleter(ptr));
507 } else {
508 auto buf = temporary_buffer<char_type>(u.internal.size);
509 std::copy(u.internal.str, u.internal.str + u.internal.size, buf.get_write());
510 u.internal.size = 0;
511 if (NulTerminate) {
512 u.internal.str[0] = '\0';
513 }
514 return buf;
515 }
516 }
f67539c2 517 int compare(std::basic_string_view<char_type, traits_type> x) const noexcept {
11fdf7f2
TL
518 auto n = traits_type::compare(begin(), x.begin(), std::min(size(), x.size()));
519 if (n != 0) {
520 return n;
521 }
522 if (size() < x.size()) {
523 return -1;
524 } else if (size() > x.size()) {
525 return 1;
526 } else {
527 return 0;
528 }
529 }
530
f67539c2 531 int compare(size_t pos, size_t sz, std::basic_string_view<char_type, traits_type> x) const {
11fdf7f2 532 if (pos > size()) {
f67539c2 533 internal::throw_sstring_out_of_range();
11fdf7f2
TL
534 }
535
536 sz = std::min(size() - pos, sz);
537 auto n = traits_type::compare(begin() + pos, x.begin(), std::min(sz, x.size()));
538 if (n != 0) {
539 return n;
540 }
541 if (sz < x.size()) {
542 return -1;
543 } else if (sz > x.size()) {
544 return 1;
545 } else {
546 return 0;
547 }
548 }
549
550 void swap(basic_sstring& x) noexcept {
551 contents tmp;
552 tmp = x.u;
553 x.u = u;
554 u = tmp;
555 }
f67539c2 556 char_type* data() noexcept {
11fdf7f2
TL
557 return str();
558 }
f67539c2 559 const char_type* data() const noexcept {
11fdf7f2
TL
560 return str();
561 }
f67539c2 562 const char_type* c_str() const noexcept {
11fdf7f2
TL
563 return str();
564 }
f67539c2
TL
565 const char_type* begin() const noexcept { return str(); }
566 const char_type* end() const noexcept { return str() + size(); }
567 const char_type* cbegin() const noexcept { return str(); }
568 const char_type* cend() const noexcept { return str() + size(); }
569 char_type* begin() noexcept { return str(); }
570 char_type* end() noexcept { return str() + size(); }
571 bool operator==(const basic_sstring& x) const noexcept {
11fdf7f2
TL
572 return size() == x.size() && std::equal(begin(), end(), x.begin());
573 }
f67539c2 574 bool operator!=(const basic_sstring& x) const noexcept {
11fdf7f2
TL
575 return !operator==(x);
576 }
f67539c2 577 bool operator<(const basic_sstring& x) const noexcept {
11fdf7f2
TL
578 return compare(x) < 0;
579 }
580 basic_sstring operator+(const basic_sstring& x) const {
581 basic_sstring ret(initialized_later(), size() + x.size());
582 std::copy(begin(), end(), ret.begin());
583 std::copy(x.begin(), x.end(), ret.begin() + size());
584 return ret;
585 }
586 basic_sstring& operator+=(const basic_sstring& x) {
587 return *this = *this + x;
588 }
f67539c2 589 char_type& operator[](size_type pos) noexcept {
11fdf7f2
TL
590 return str()[pos];
591 }
f67539c2 592 const char_type& operator[](size_type pos) const noexcept {
11fdf7f2
TL
593 return str()[pos];
594 }
595
f67539c2
TL
596 operator std::basic_string_view<char_type>() const noexcept {
597 // we assume that std::basic_string_view<char_type>(str(), size())
598 // won't throw, although it is not specified as noexcept in
599 // https://en.cppreference.com/w/cpp/string/basic_string_view/basic_string_view
600 // at this time (C++20).
601 //
602 // This is similar to std::string operator std::basic_string_view:
603 // https://en.cppreference.com/w/cpp/string/basic_string/operator_basic_string_view
604 // that is specified as noexcept too.
605 static_assert(noexcept(std::basic_string_view<char_type>(str(), size())));
606 return std::basic_string_view<char_type>(str(), size());
11fdf7f2 607 }
11fdf7f2
TL
608};
609template <typename char_type, typename Size, Size max_size, bool NulTerminate>
610constexpr Size basic_sstring<char_type, Size, max_size, NulTerminate>::npos;
611
1e59de90
TL
612namespace internal {
613template <class T> struct is_sstring : std::false_type {};
614template <typename char_type, typename Size, Size max_size, bool NulTerminate>
615struct is_sstring<basic_sstring<char_type, Size, max_size, NulTerminate>> : std::true_type {};
f67539c2
TL
616}
617
1e59de90
TL
618template <typename string_type = sstring>
619string_type uninitialized_string(size_t size) {
620 if constexpr (internal::is_sstring<string_type>::value) {
621 return string_type(typename string_type::initialized_later(), size);
622 } else {
623 string_type ret;
624#ifdef __cpp_lib_string_resize_and_overwrite
625 ret.resize_and_overwrite(size, [](string_type::value_type*, string_type::size_type n) { return n; });
626#else
627 ret.resize(size);
628#endif
629 return ret;
630 }
f67539c2
TL
631}
632
11fdf7f2
TL
633template <typename char_type, typename size_type, size_type Max, size_type N, bool NulTerminate>
634inline
635basic_sstring<char_type, size_type, Max, NulTerminate>
636operator+(const char(&s)[N], const basic_sstring<char_type, size_type, Max, NulTerminate>& t) {
637 using sstring = basic_sstring<char_type, size_type, Max, NulTerminate>;
638 // don't copy the terminating NUL character
639 sstring ret(typename sstring::initialized_later(), N-1 + t.size());
640 auto p = std::copy(std::begin(s), std::end(s)-1, ret.begin());
641 std::copy(t.begin(), t.end(), p);
642 return ret;
643}
644
1e59de90 645template <typename T>
11fdf7f2 646static inline
1e59de90
TL
647size_t constexpr str_len(const T& s) {
648 return std::string_view(s).size();
11fdf7f2
TL
649}
650
651template <typename char_type, typename size_type, size_type max_size>
652inline
653void swap(basic_sstring<char_type, size_type, max_size>& x,
654 basic_sstring<char_type, size_type, max_size>& y) noexcept
655{
656 return x.swap(y);
657}
658
659template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, typename char_traits>
660inline
661std::basic_ostream<char_type, char_traits>&
662operator<<(std::basic_ostream<char_type, char_traits>& os,
663 const basic_sstring<char_type, size_type, max_size, NulTerminate>& s) {
664 return os.write(s.begin(), s.size());
665}
666
667template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, typename char_traits>
668inline
669std::basic_istream<char_type, char_traits>&
670operator>>(std::basic_istream<char_type, char_traits>& is,
671 basic_sstring<char_type, size_type, max_size, NulTerminate>& s) {
672 std::string tmp;
673 is >> tmp;
674 s = tmp;
675 return is;
676}
677
678}
679
680namespace std {
681
682template <typename char_type, typename size_type, size_type max_size, bool NulTerminate>
683struct hash<seastar::basic_sstring<char_type, size_type, max_size, NulTerminate>> {
684 size_t operator()(const seastar::basic_sstring<char_type, size_type, max_size, NulTerminate>& s) const {
f67539c2 685 return std::hash<std::basic_string_view<char_type>>()(s);
11fdf7f2
TL
686 }
687};
688
689}
690
691namespace seastar {
692
1e59de90 693template <typename T>
11fdf7f2 694static inline
1e59de90
TL
695void copy_str_to(char*& dst, const T& s) {
696 std::string_view v(s);
697 dst = std::copy(v.begin(), v.end(), dst);
11fdf7f2
TL
698}
699
700template <typename String = sstring, typename... Args>
701static String make_sstring(Args&&... args)
702{
1e59de90
TL
703 String ret = uninitialized_string<String>((str_len(args) + ...));
704 auto dst = ret.data();
705 (copy_str_to(dst, args), ...);
11fdf7f2
TL
706 return ret;
707}
708
f67539c2 709namespace internal {
11fdf7f2 710template <typename string_type, typename T>
1e59de90
TL
711string_type to_sstring(T value) {
712 auto size = fmt::formatted_size("{}", value);
713 auto formatted = uninitialized_string<string_type>(size);
714 fmt::format_to(formatted.data(), "{}", value);
715 return formatted;
f67539c2
TL
716}
717
718template <typename string_type>
719string_type to_sstring(const char* value) {
720 return string_type(value);
721}
722
723template <typename string_type>
724string_type to_sstring(sstring value) {
725 return value;
726}
727
728template <typename string_type>
729string_type to_sstring(const temporary_buffer<char>& buf) {
730 return string_type(buf.get(), buf.size());
731}
732}
733
734template <typename string_type = sstring, typename T>
735string_type to_sstring(T value) {
736 return internal::to_sstring<string_type>(value);
737}
11fdf7f2
TL
738}
739
740namespace std {
741template <typename T>
742inline
743std::ostream& operator<<(std::ostream& os, const std::vector<T>& v) {
744 bool first = true;
745 os << "{";
746 for (auto&& elem : v) {
747 if (!first) {
748 os << ", ";
749 } else {
750 first = false;
751 }
752 os << elem;
753 }
754 os << "}";
755 return os;
756}
757
758template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
759std::ostream& operator<<(std::ostream& os, const std::unordered_map<Key, T, Hash, KeyEqual, Allocator>& v) {
760 bool first = true;
761 os << "{";
762 for (auto&& elem : v) {
763 if (!first) {
764 os << ", ";
765 } else {
766 first = false;
767 }
1e59de90 768 os << "{" << elem.first << " -> " << elem.second << "}";
11fdf7f2
TL
769 }
770 os << "}";
771 return os;
772}
773}
1e59de90
TL
774
775#if FMT_VERSION >= 90000
776
777template <typename char_type, typename Size, Size max_size, bool NulTerminate>
778struct fmt::formatter<seastar::basic_sstring<char_type, Size, max_size, NulTerminate>> : fmt::ostream_formatter {};
779
780#endif