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