]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
1 | // |
2 | // Copyright (c) 2013-2017 Vinnie Falco (vinnie dot falco at gmail dot com) | |
3 | // | |
4 | // Distributed under the Boost Software License, Version 1.0. (See accompanying | |
5 | // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | |
6 | // | |
7 | ||
8 | #ifndef BEAST_WEBSOCKET_STATIC_STRING_HPP | |
9 | #define BEAST_WEBSOCKET_STATIC_STRING_HPP | |
10 | ||
11 | #include <beast/config.hpp> | |
12 | #include <beast/core/detail/type_traits.hpp> | |
13 | #include <array> | |
14 | #include <cstdint> | |
15 | #include <iterator> | |
16 | #include <stdexcept> | |
17 | #include <string> | |
18 | ||
19 | namespace beast { | |
20 | ||
21 | /** A string with a fixed-size storage area. | |
22 | ||
23 | These objects behave like `std::string` except that the storage | |
24 | is not dynamically allocated but rather fixed in size. | |
25 | ||
26 | These strings offer performance advantages when a protocol | |
27 | imposes a natural small upper limit on the size of a value. | |
28 | ||
29 | @note The stored string is always null-terminated. | |
30 | */ | |
31 | template< | |
32 | std::size_t N, | |
33 | class CharT = char, | |
34 | class Traits = std::char_traits<CharT>> | |
35 | class static_string | |
36 | { | |
37 | template<std::size_t, class, class> | |
38 | friend class static_string; | |
39 | ||
40 | std::size_t n_; | |
41 | std::array<CharT, N+1> s_; | |
42 | ||
43 | public: | |
44 | using traits_type = Traits; | |
45 | using value_type = typename Traits::char_type; | |
46 | using size_type = std::size_t; | |
47 | using difference_type = std::ptrdiff_t; | |
48 | using pointer = value_type*; | |
49 | using reference = value_type&; | |
50 | using const_pointer = value_type const*; | |
51 | using const_reference = value_type const&; | |
52 | using iterator = value_type*; | |
53 | using const_iterator = value_type const*; | |
54 | using reverse_iterator = | |
55 | std::reverse_iterator<iterator>; | |
56 | using const_reverse_iterator = | |
57 | std::reverse_iterator<const_iterator>; | |
58 | ||
59 | /** Default constructor. | |
60 | ||
61 | The string is initially empty, and null terminated. | |
62 | */ | |
63 | static_string(); | |
64 | ||
65 | /// Copy constructor. | |
66 | static_string(static_string const& s); | |
67 | ||
68 | /// Copy constructor. | |
69 | template<std::size_t M> | |
70 | static_string(static_string<M, CharT, Traits> const& s); | |
71 | ||
72 | /// Copy assignment. | |
73 | static_string& | |
74 | operator=(static_string const& s); | |
75 | ||
76 | /// Copy assignment. | |
77 | template<std::size_t M> | |
78 | static_string& | |
79 | operator=(static_string<M, CharT, Traits> const& s); | |
80 | ||
81 | /// Construct from string literal. | |
82 | template<std::size_t M> | |
83 | static_string(const CharT (&s)[M]); | |
84 | ||
85 | /// Assign from string literal. | |
86 | template<std::size_t M> | |
87 | static_string& operator=(const CharT (&s)[M]); | |
88 | ||
89 | /// Access specified character with bounds checking. | |
90 | reference | |
91 | at(size_type pos); | |
92 | ||
93 | /// Access specified character with bounds checking. | |
94 | const_reference | |
95 | at(size_type pos) const; | |
96 | ||
97 | /// Access specified character. | |
98 | reference | |
99 | operator[](size_type pos) | |
100 | { | |
101 | return s_[pos]; | |
102 | } | |
103 | ||
104 | /// Access specified character. | |
105 | const_reference | |
106 | operator[](size_type pos) const | |
107 | { | |
108 | return s_[pos]; | |
109 | } | |
110 | ||
111 | /// Accesses the first character. | |
112 | CharT& | |
113 | front() | |
114 | { | |
115 | return s_[0]; | |
116 | } | |
117 | ||
118 | /// Accesses the first character. | |
119 | CharT const& | |
120 | front() const | |
121 | { | |
122 | return s_[0]; | |
123 | } | |
124 | ||
125 | /// Accesses the last character. | |
126 | CharT& | |
127 | back() | |
128 | { | |
129 | return s_[n_-1]; | |
130 | } | |
131 | ||
132 | /// Accesses the last character. | |
133 | CharT const& | |
134 | back() const | |
135 | { | |
136 | return s_[n_-1]; | |
137 | } | |
138 | ||
139 | /// Returns a pointer to the first character of a string. | |
140 | CharT* | |
141 | data() | |
142 | { | |
143 | return &s_[0]; | |
144 | } | |
145 | ||
146 | /// Returns a pointer to the first character of a string. | |
147 | CharT const* | |
148 | data() const | |
149 | { | |
150 | return &s_[0]; | |
151 | } | |
152 | ||
153 | /// Returns a non-modifiable standard C character array version of the string. | |
154 | CharT const* | |
155 | c_str() const | |
156 | { | |
157 | return &s_[0]; | |
158 | } | |
159 | ||
160 | /// Returns an iterator to the beginning. | |
161 | iterator | |
162 | begin() | |
163 | { | |
164 | return &s_[0]; | |
165 | } | |
166 | ||
167 | /// Returns an iterator to the beginning. | |
168 | const_iterator | |
169 | begin() const | |
170 | { | |
171 | return &s_[0]; | |
172 | } | |
173 | ||
174 | /// Returns an iterator to the beginning. | |
175 | const_iterator | |
176 | cbegin() const | |
177 | { | |
178 | return &s_[0]; | |
179 | } | |
180 | ||
181 | /// Returns an iterator to the end. | |
182 | iterator | |
183 | end() | |
184 | { | |
185 | return &s_[n_]; | |
186 | } | |
187 | ||
188 | /// Returns an iterator to the end. | |
189 | const_iterator | |
190 | end() const | |
191 | { | |
192 | return &s_[n_]; | |
193 | } | |
194 | ||
195 | /// Returns an iterator to the end. | |
196 | const_iterator | |
197 | cend() const | |
198 | { | |
199 | return &s_[n_]; | |
200 | } | |
201 | ||
202 | /// Returns a reverse iterator to the beginning. | |
203 | reverse_iterator | |
204 | rbegin() | |
205 | { | |
206 | return reverse_iterator{end()}; | |
207 | } | |
208 | ||
209 | /// Returns a reverse iterator to the beginning. | |
210 | const_reverse_iterator | |
211 | rbegin() const | |
212 | { | |
213 | return const_reverse_iterator{cend()}; | |
214 | } | |
215 | ||
216 | /// Returns a reverse iterator to the beginning. | |
217 | const_reverse_iterator | |
218 | crbegin() const | |
219 | { | |
220 | return const_reverse_iterator{cend()}; | |
221 | } | |
222 | ||
223 | /// Returns a reverse iterator to the end. | |
224 | reverse_iterator | |
225 | rend() | |
226 | { | |
227 | return reverse_iterator{begin()}; | |
228 | } | |
229 | ||
230 | /// Returns a reverse iterator to the end. | |
231 | const_reverse_iterator | |
232 | rend() const | |
233 | { | |
234 | return const_reverse_iterator{cbegin()}; | |
235 | } | |
236 | ||
237 | /// Returns a reverse iterator to the end. | |
238 | const_reverse_iterator | |
239 | crend() const | |
240 | { | |
241 | return const_reverse_iterator{cbegin()}; | |
242 | } | |
243 | ||
244 | /// Returns `true` if the string is empty. | |
245 | bool | |
246 | empty() const | |
247 | { | |
248 | return n_ == 0; | |
249 | } | |
250 | ||
251 | /// Returns the number of characters, excluding the null terminator. | |
252 | size_type | |
253 | size() const | |
254 | { | |
255 | return n_; | |
256 | } | |
257 | ||
258 | /// Returns the maximum number of characters that can be stored, excluding the null terminator. | |
259 | size_type constexpr | |
260 | max_size() const | |
261 | { | |
262 | return N; | |
263 | } | |
264 | ||
265 | /// Returns the number of characters that can be held in currently allocated storage. | |
266 | size_type | |
267 | capacity() const | |
268 | { | |
269 | return N; | |
270 | } | |
271 | ||
272 | /// Clears the contents. | |
273 | void | |
274 | clear() | |
275 | { | |
276 | resize(0); | |
277 | } | |
278 | ||
279 | /** Changes the number of characters stored. | |
280 | ||
281 | @note No value-initialization is performed. | |
282 | */ | |
283 | void | |
284 | resize(std::size_t n); | |
285 | ||
286 | /** Changes the number of characters stored. | |
287 | ||
288 | If the resulting string is larger, the new | |
289 | characters are initialized to the value of `c`. | |
290 | */ | |
291 | void | |
292 | resize(std::size_t n, CharT c); | |
293 | ||
294 | /// Compare two character sequences. | |
295 | template<std::size_t M> | |
296 | int | |
297 | compare(static_string<M, CharT, Traits> const& rhs) const; | |
298 | ||
299 | /// Return the characters as a `basic_string`. | |
300 | std::basic_string<CharT, Traits> | |
301 | to_string() const | |
302 | { | |
303 | return std::basic_string< | |
304 | CharT, Traits>{&s_[0], n_}; | |
305 | } | |
306 | ||
307 | private: | |
308 | void | |
309 | assign(CharT const* s); | |
310 | }; | |
311 | ||
312 | template<std::size_t N, class CharT, class Traits> | |
313 | static_string<N, CharT, Traits>:: | |
314 | static_string() | |
315 | : n_(0) | |
316 | { | |
317 | s_[0] = 0; | |
318 | } | |
319 | ||
320 | template<std::size_t N, class CharT, class Traits> | |
321 | static_string<N, CharT, Traits>:: | |
322 | static_string(static_string const& s) | |
323 | : n_(s.n_) | |
324 | { | |
325 | Traits::copy(&s_[0], &s.s_[0], n_ + 1); | |
326 | } | |
327 | ||
328 | template<std::size_t N, class CharT, class Traits> | |
329 | template<std::size_t M> | |
330 | static_string<N, CharT, Traits>:: | |
331 | static_string(static_string<M, CharT, Traits> const& s) | |
332 | { | |
333 | if(s.size() > N) | |
334 | throw detail::make_exception<std::length_error>( | |
335 | "static_string overflow", __FILE__, __LINE__); | |
336 | n_ = s.size(); | |
337 | Traits::copy(&s_[0], &s.s_[0], n_ + 1); | |
338 | } | |
339 | ||
340 | template<std::size_t N, class CharT, class Traits> | |
341 | auto | |
342 | static_string<N, CharT, Traits>:: | |
343 | operator=(static_string const& s) -> | |
344 | static_string& | |
345 | { | |
346 | n_ = s.n_; | |
347 | Traits::copy(&s_[0], &s.s_[0], n_ + 1); | |
348 | return *this; | |
349 | } | |
350 | ||
351 | template<std::size_t N, class CharT, class Traits> | |
352 | template<std::size_t M> | |
353 | auto | |
354 | static_string<N, CharT, Traits>:: | |
355 | operator=(static_string<M, CharT, Traits> const& s) -> | |
356 | static_string& | |
357 | { | |
358 | if(s.size() > N) | |
359 | throw detail::make_exception<std::length_error>( | |
360 | "static_string overflow", __FILE__, __LINE__); | |
361 | n_ = s.size(); | |
362 | Traits::copy(&s_[0], &s.s_[0], n_ + 1); | |
363 | return *this; | |
364 | } | |
365 | ||
366 | template<std::size_t N, class CharT, class Traits> | |
367 | template<std::size_t M> | |
368 | static_string<N, CharT, Traits>:: | |
369 | static_string(const CharT (&s)[M]) | |
370 | : n_(M-1) | |
371 | { | |
372 | static_assert(M-1 <= N, | |
373 | "static_string overflow"); | |
374 | Traits::copy(&s_[0], &s[0], M); | |
375 | } | |
376 | ||
377 | template<std::size_t N, class CharT, class Traits> | |
378 | template<std::size_t M> | |
379 | auto | |
380 | static_string<N, CharT, Traits>:: | |
381 | operator=(const CharT (&s)[M]) -> | |
382 | static_string& | |
383 | { | |
384 | static_assert(M-1 <= N, | |
385 | "static_string overflow"); | |
386 | n_ = M-1; | |
387 | Traits::copy(&s_[0], &s[0], M); | |
388 | return *this; | |
389 | } | |
390 | ||
391 | template<std::size_t N, class CharT, class Traits> | |
392 | auto | |
393 | static_string<N, CharT, Traits>:: | |
394 | at(size_type pos) -> | |
395 | reference | |
396 | { | |
397 | if(pos >= n_) | |
398 | throw detail::make_exception<std::out_of_range>( | |
399 | "invalid pos", __FILE__, __LINE__); | |
400 | return s_[pos]; | |
401 | } | |
402 | ||
403 | template<std::size_t N, class CharT, class Traits> | |
404 | auto | |
405 | static_string<N, CharT, Traits>:: | |
406 | at(size_type pos) const -> | |
407 | const_reference | |
408 | { | |
409 | if(pos >= n_) | |
410 | throw detail::make_exception<std::out_of_range>( | |
411 | "static_string::at", __FILE__, __LINE__); | |
412 | return s_[pos]; | |
413 | } | |
414 | ||
415 | template<std::size_t N, class CharT, class Traits> | |
416 | void | |
417 | static_string<N, CharT, Traits>:: | |
418 | resize(std::size_t n) | |
419 | { | |
420 | if(n > N) | |
421 | throw detail::make_exception<std::length_error>( | |
422 | "static_string overflow", __FILE__, __LINE__); | |
423 | n_ = n; | |
424 | s_[n_] = 0; | |
425 | } | |
426 | ||
427 | template<std::size_t N, class CharT, class Traits> | |
428 | void | |
429 | static_string<N, CharT, Traits>:: | |
430 | resize(std::size_t n, CharT c) | |
431 | { | |
432 | if(n > N) | |
433 | throw detail::make_exception<std::length_error>( | |
434 | "static_string overflow", __FILE__, __LINE__); | |
435 | if(n > n_) | |
436 | Traits::assign(&s_[n_], n - n_, c); | |
437 | n_ = n; | |
438 | s_[n_] = 0; | |
439 | } | |
440 | ||
441 | template<std::size_t N, class CharT, class Traits> | |
442 | template<std::size_t M> | |
443 | int | |
444 | static_string<N, CharT, Traits>:: | |
445 | compare(static_string<M, CharT, Traits> const& rhs) const | |
446 | { | |
447 | if(size() < rhs.size()) | |
448 | { | |
449 | auto const v = Traits::compare( | |
450 | data(), rhs.data(), size()); | |
451 | if(v == 0) | |
452 | return -1; | |
453 | return v; | |
454 | } | |
455 | else if(size() > rhs.size()) | |
456 | { | |
457 | auto const v = Traits::compare( | |
458 | data(), rhs.data(), rhs.size()); | |
459 | if(v == 0) | |
460 | return 1; | |
461 | return v; | |
462 | } | |
463 | return Traits::compare(data(), rhs.data(), size()); | |
464 | } | |
465 | ||
466 | template<std::size_t N, class CharT, class Traits> | |
467 | void | |
468 | static_string<N, CharT, Traits>:: | |
469 | assign(CharT const* s) | |
470 | { | |
471 | auto const n = Traits::length(s); | |
472 | if(n > N) | |
473 | throw detail::make_exception<std::out_of_range>( | |
474 | "too large", __FILE__, __LINE__); | |
475 | n_ = n; | |
476 | Traits::copy(&s_[0], s, n_ + 1); | |
477 | } | |
478 | ||
479 | namespace detail { | |
480 | ||
481 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
482 | int | |
483 | compare( | |
484 | static_string<N, CharT, Traits> const& lhs, | |
485 | const CharT (&s)[M]) | |
486 | { | |
487 | if(lhs.size() < M-1) | |
488 | { | |
489 | auto const v = Traits::compare( | |
490 | lhs.data(), &s[0], lhs.size()); | |
491 | if(v == 0) | |
492 | return -1; | |
493 | return v; | |
494 | } | |
495 | else if(lhs.size() > M-1) | |
496 | { | |
497 | auto const v = Traits::compare( | |
498 | lhs.data(), &s[0], M-1); | |
499 | if(v == 0) | |
500 | return 1; | |
501 | return v; | |
502 | } | |
503 | return Traits::compare(lhs.data(), &s[0], lhs.size()); | |
504 | } | |
505 | ||
506 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
507 | inline | |
508 | int | |
509 | compare( | |
510 | const CharT (&s)[M], | |
511 | static_string<N, CharT, Traits> const& rhs) | |
512 | { | |
513 | return -compare(rhs, s); | |
514 | } | |
515 | ||
516 | } // detail | |
517 | ||
518 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
519 | bool | |
520 | operator==( | |
521 | static_string<N, CharT, Traits> const& lhs, | |
522 | static_string<M, CharT, Traits> const& rhs) | |
523 | { | |
524 | return lhs.compare(rhs) == 0; | |
525 | } | |
526 | ||
527 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
528 | bool | |
529 | operator!=( | |
530 | static_string<N, CharT, Traits> const& lhs, | |
531 | static_string<M, CharT, Traits> const& rhs) | |
532 | { | |
533 | return lhs.compare(rhs) != 0; | |
534 | } | |
535 | ||
536 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
537 | bool | |
538 | operator<( | |
539 | static_string<N, CharT, Traits> const& lhs, | |
540 | static_string<M, CharT, Traits> const& rhs) | |
541 | { | |
542 | return lhs.compare(rhs) < 0; | |
543 | } | |
544 | ||
545 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
546 | bool | |
547 | operator<=( | |
548 | static_string<N, CharT, Traits> const& lhs, | |
549 | static_string<M, CharT, Traits> const& rhs) | |
550 | { | |
551 | return lhs.compare(rhs) <= 0; | |
552 | } | |
553 | ||
554 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
555 | bool | |
556 | operator>( | |
557 | static_string<N, CharT, Traits> const& lhs, | |
558 | static_string<M, CharT, Traits> const& rhs) | |
559 | { | |
560 | return lhs.compare(rhs) > 0; | |
561 | } | |
562 | ||
563 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
564 | bool | |
565 | operator>=( | |
566 | static_string<N, CharT, Traits> const& lhs, | |
567 | static_string<M, CharT, Traits> const& rhs) | |
568 | { | |
569 | return lhs.compare(rhs) >= 0; | |
570 | } | |
571 | ||
572 | //--- | |
573 | ||
574 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
575 | bool | |
576 | operator==( | |
577 | const CharT (&s)[N], | |
578 | static_string<M, CharT, Traits> const& rhs) | |
579 | { | |
580 | return detail::compare(s, rhs) == 0; | |
581 | } | |
582 | ||
583 | template<std::size_t N, class CharT, class Traits, std::size_t M> | |
584 | bool | |
585 | operator==( | |
586 | static_string<N, CharT, Traits> const& lhs, | |
587 | const CharT (&s)[M]) | |
588 | { | |
589 | return detail::compare(lhs, s) == 0; | |
590 | } | |
591 | ||
592 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
593 | bool | |
594 | operator!=( | |
595 | const CharT (&s)[N], | |
596 | static_string<M, CharT, Traits> const& rhs) | |
597 | { | |
598 | return detail::compare(s, rhs) != 0; | |
599 | } | |
600 | ||
601 | template<std::size_t N, class CharT, class Traits, std::size_t M> | |
602 | bool | |
603 | operator!=( | |
604 | static_string<N, CharT, Traits> const& lhs, | |
605 | const CharT (&s)[M]) | |
606 | { | |
607 | return detail::compare(lhs, s) != 0; | |
608 | } | |
609 | ||
610 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
611 | bool | |
612 | operator<( | |
613 | const CharT (&s)[N], | |
614 | static_string<M, CharT, Traits> const& rhs) | |
615 | { | |
616 | return detail::compare(s, rhs) < 0; | |
617 | } | |
618 | ||
619 | template<std::size_t N, class CharT, class Traits, std::size_t M> | |
620 | bool | |
621 | operator<( | |
622 | static_string<N, CharT, Traits> const& lhs, | |
623 | const CharT (&s)[M]) | |
624 | { | |
625 | return detail::compare(lhs, s) < 0; | |
626 | } | |
627 | ||
628 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
629 | bool | |
630 | operator<=( | |
631 | const CharT (&s)[N], | |
632 | static_string<M, CharT, Traits> const& rhs) | |
633 | { | |
634 | return detail::compare(s, rhs) <= 0; | |
635 | } | |
636 | ||
637 | template<std::size_t N, class CharT, class Traits, std::size_t M> | |
638 | bool | |
639 | operator<=( | |
640 | static_string<N, CharT, Traits> const& lhs, | |
641 | const CharT (&s)[M]) | |
642 | { | |
643 | return detail::compare(lhs, s) <= 0; | |
644 | } | |
645 | ||
646 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
647 | bool | |
648 | operator>( | |
649 | const CharT (&s)[N], | |
650 | static_string<M, CharT, Traits> const& rhs) | |
651 | { | |
652 | return detail::compare(s, rhs) > 0; | |
653 | } | |
654 | ||
655 | template<std::size_t N, class CharT, class Traits, std::size_t M> | |
656 | bool | |
657 | operator>( | |
658 | static_string<N, CharT, Traits> const& lhs, | |
659 | const CharT (&s)[M]) | |
660 | { | |
661 | return detail::compare(lhs, s) > 0; | |
662 | } | |
663 | ||
664 | template<std::size_t N, std::size_t M, class CharT, class Traits> | |
665 | bool | |
666 | operator>=( | |
667 | const CharT (&s)[N], | |
668 | static_string<M, CharT, Traits> const& rhs) | |
669 | { | |
670 | return detail::compare(s, rhs) >= 0; | |
671 | } | |
672 | ||
673 | template<std::size_t N, class CharT, class Traits, std::size_t M> | |
674 | bool | |
675 | operator>=( | |
676 | static_string<N, CharT, Traits> const& lhs, | |
677 | const CharT (&s)[M]) | |
678 | { | |
679 | return detail::compare(lhs, s) >= 0; | |
680 | } | |
681 | ||
682 | } // beast | |
683 | ||
684 | #endif |