]>
Commit | Line | Data |
---|---|---|
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 | ||
42 | namespace seastar { | |
43 | ||
44 | template <typename char_type, typename Size, Size max_size, bool NulTerminate = true> | |
45 | class 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 | 52 | using sstring = basic_sstring<char, uint32_t, 15>; |
f67539c2 TL |
53 | #else |
54 | using sstring = std::string; | |
55 | #endif | |
56 | ||
57 | namespace 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 | |
63 | template <typename char_type, typename Size, Size max_size, bool NulTerminate> | |
64 | class 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 |
96 | public: |
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); } | |
111 | public: | |
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 | }; |
609 | template <typename char_type, typename Size, Size max_size, bool NulTerminate> | |
610 | constexpr Size basic_sstring<char_type, Size, max_size, NulTerminate>::npos; | |
611 | ||
1e59de90 TL |
612 | namespace internal { |
613 | template <class T> struct is_sstring : std::false_type {}; | |
614 | template <typename char_type, typename Size, Size max_size, bool NulTerminate> | |
615 | struct is_sstring<basic_sstring<char_type, Size, max_size, NulTerminate>> : std::true_type {}; | |
f67539c2 TL |
616 | } |
617 | ||
1e59de90 TL |
618 | template <typename string_type = sstring> |
619 | string_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 |
633 | template <typename char_type, typename size_type, size_type Max, size_type N, bool NulTerminate> |
634 | inline | |
635 | basic_sstring<char_type, size_type, Max, NulTerminate> | |
636 | operator+(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 | 645 | template <typename T> |
11fdf7f2 | 646 | static inline |
1e59de90 TL |
647 | size_t constexpr str_len(const T& s) { |
648 | return std::string_view(s).size(); | |
11fdf7f2 TL |
649 | } |
650 | ||
651 | template <typename char_type, typename size_type, size_type max_size> | |
652 | inline | |
653 | void 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 | ||
659 | template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, typename char_traits> | |
660 | inline | |
661 | std::basic_ostream<char_type, char_traits>& | |
662 | operator<<(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 | ||
667 | template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, typename char_traits> | |
668 | inline | |
669 | std::basic_istream<char_type, char_traits>& | |
670 | operator>>(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 | ||
680 | namespace std { | |
681 | ||
682 | template <typename char_type, typename size_type, size_type max_size, bool NulTerminate> | |
683 | struct 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 | ||
691 | namespace seastar { | |
692 | ||
1e59de90 | 693 | template <typename T> |
11fdf7f2 | 694 | static inline |
1e59de90 TL |
695 | void 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 | ||
700 | template <typename String = sstring, typename... Args> | |
701 | static 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 | 709 | namespace internal { |
11fdf7f2 | 710 | template <typename string_type, typename T> |
1e59de90 TL |
711 | string_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 | ||
718 | template <typename string_type> | |
719 | string_type to_sstring(const char* value) { | |
720 | return string_type(value); | |
721 | } | |
722 | ||
723 | template <typename string_type> | |
724 | string_type to_sstring(sstring value) { | |
725 | return value; | |
726 | } | |
727 | ||
728 | template <typename string_type> | |
729 | string_type to_sstring(const temporary_buffer<char>& buf) { | |
730 | return string_type(buf.get(), buf.size()); | |
731 | } | |
732 | } | |
733 | ||
734 | template <typename string_type = sstring, typename T> | |
735 | string_type to_sstring(T value) { | |
736 | return internal::to_sstring<string_type>(value); | |
737 | } | |
11fdf7f2 TL |
738 | } |
739 | ||
740 | namespace std { | |
741 | template <typename T> | |
742 | inline | |
743 | std::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 | ||
758 | template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator> | |
759 | std::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 | ||
777 | template <typename char_type, typename Size, Size max_size, bool NulTerminate> | |
778 | struct fmt::formatter<seastar::basic_sstring<char_type, Size, max_size, NulTerminate>> : fmt::ostream_formatter {}; | |
779 | ||
780 | #endif |