]> git.proxmox.com Git - ceph.git/blame - ceph/src/seastar/include/seastar/core/sstring.hh
update source to Ceph Pacific 16.2.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>
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
40namespace seastar {
41
42template <typename char_type, typename Size, Size max_size, bool NulTerminate = true>
43class basic_sstring;
44
f67539c2 45#ifdef SEASTAR_SSTRING
11fdf7f2 46using sstring = basic_sstring<char, uint32_t, 15>;
f67539c2
TL
47#else
48using sstring = std::string;
49#endif
50
51namespace 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
57template <typename char_type, typename Size, Size max_size, bool NulTerminate>
58class 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
90public:
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); }
105public:
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};
568template <typename char_type, typename Size, Size max_size, bool NulTerminate>
569constexpr Size basic_sstring<char_type, Size, max_size, NulTerminate>::npos;
570
f67539c2
TL
571template <typename string_type = sstring>
572string_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
579template <typename char_type, typename Size, Size max_size, bool NulTerminate>
580basic_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
585template <typename char_type, typename size_type, size_type Max, size_type N, bool NulTerminate>
586inline
587basic_sstring<char_type, size_type, Max, NulTerminate>
588operator+(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 597static inline
f67539c2
TL
598size_t str_len() {
599 return 0;
600}
11fdf7f2 601
f67539c2 602template <typename First, typename... Tail>
11fdf7f2 603static inline
f67539c2
TL
604size_t str_len(const First& first, const Tail&... tail) {
605 return std::string_view(first).size() + str_len(tail...);
11fdf7f2
TL
606}
607
608template <typename char_type, typename size_type, size_type max_size>
609inline
610void 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
616template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, typename char_traits>
617inline
618std::basic_ostream<char_type, char_traits>&
619operator<<(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
624template <typename char_type, typename size_type, size_type max_size, bool NulTerminate, typename char_traits>
625inline
626std::basic_istream<char_type, char_traits>&
627operator>>(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
637namespace std {
638
639template <typename char_type, typename size_type, size_type max_size, bool NulTerminate>
640struct 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
648namespace seastar {
649
650static inline
651char* copy_str_to(char* dst) {
652 return dst;
653}
654
655template <typename Head, typename... Tail>
656static inline
657char* 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
662template <typename String = sstring, typename... Args>
663static 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 670namespace internal {
11fdf7f2 671template <typename string_type, typename T>
f67539c2
TL
672string_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
679template <typename string_type>
680string_type to_sstring(int value) {
681 return to_sstring_sprintf<string_type>(value, "%d");
682}
683
684template <typename string_type>
685string_type to_sstring(unsigned value) {
686 return to_sstring_sprintf<string_type>(value, "%u");
687}
688
689template <typename string_type>
690string_type to_sstring(long value) {
691 return to_sstring_sprintf<string_type>(value, "%ld");
692}
693
694template <typename string_type>
695string_type to_sstring(unsigned long value) {
696 return to_sstring_sprintf<string_type>(value, "%lu");
11fdf7f2
TL
697}
698
f67539c2
TL
699template <typename string_type>
700string_type to_sstring(long long value) {
701 return to_sstring_sprintf<string_type>(value, "%lld");
702}
703
704template <typename string_type>
705string_type to_sstring(unsigned long long value) {
706 return to_sstring_sprintf<string_type>(value, "%llu");
707}
708
709template <typename string_type>
710string_type to_sstring(float value) {
711 return to_sstring_sprintf<string_type>(value, "%g");
712}
713
714template <typename string_type>
715string_type to_sstring(double value) {
716 return to_sstring_sprintf<string_type>(value, "%g");
717}
718
719template <typename string_type>
720string_type to_sstring(long double value) {
721 return to_sstring_sprintf<string_type>(value, "%Lg");
722}
723
724template <typename string_type>
725string_type to_sstring(const char* value) {
726 return string_type(value);
727}
728
729template <typename string_type>
730string_type to_sstring(sstring value) {
731 return value;
732}
733
734template <typename string_type>
735string_type to_sstring(const temporary_buffer<char>& buf) {
736 return string_type(buf.get(), buf.size());
737}
738}
739
740template <typename string_type = sstring, typename T>
741string_type to_sstring(T value) {
742 return internal::to_sstring<string_type>(value);
743}
11fdf7f2
TL
744}
745
746namespace std {
747template <typename T>
748inline
749std::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
764template <typename Key, typename T, typename Hash, typename KeyEqual, typename Allocator>
765std::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}