source: mainline/uspace/lib/cpp/include/impl/string.hpp@ 9315761

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 9315761 was dc0fff11, checked in by Dzejrou <dzejrou@…>, 7 years ago

cpp: fixed some bugs found by the string tests

  • Property mode set to 100644
File size: 43.2 KB
Line 
1/*
2 * Copyright (c) 2017 Jaroslav Jindrak
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#ifndef LIBCPP_STRING
30#define LIBCPP_STRING
31
32#include <algorithm>
33#include <iosfwd>
34#include <iterator>
35#include <cstdio>
36#include <cstdlib>
37#include <cstring>
38#include <memory>
39#include <utility>
40
41#include <initializer_list>
42
43namespace std
44{
45
46 /**
47 * 21.2, char_traits:
48 */
49
50 template<class Char>
51 struct char_traits;
52
53 /**
54 * 21.2.3, char_traits specializations:
55 */
56
57 template<>
58 struct char_traits<char>
59 {
60 using char_type = char;
61 using int_type = int;
62 using off_type = streamoff;
63 using pos_type = streampos;
64 /* using state_type = mbstate_t; */
65
66 static void assign(char_type& c1, char_type& c2) noexcept
67 {
68 c1 = c2;
69 }
70
71 static constexpr bool eq(char_type c1, char_type c2) noexcept
72 {
73 return c1 == c2;
74 }
75
76 static constexpr bool lt(char_type c1, char_type c2) noexcept
77 {
78 return c1 < c2;
79 }
80
81 static int compare(const char_type* s1, const char_type* s2, size_t n)
82 {
83 return std::str_lcmp(s1, s2, n);
84 }
85
86 static size_t length(const char_type* s)
87 {
88 return std::str_size(s) + 1;
89 }
90
91 static const char_type* find(const char_type* s, size_t n, const char_type& c)
92 {
93 size_t i{};
94 while (i++ < n)
95 {
96 if (s[i] == c)
97 return s + i;
98 }
99
100 return nullptr;
101 }
102
103 static char_type* move(char_type* s1, const char_type* s2, size_t n)
104 {
105 return static_cast<char_type*>(memmove(s1, s2, n));
106 }
107
108 static char_type* copy(char_type* s1, const char_type* s2, size_t n)
109 {
110 return static_cast<char_type*>(memcpy(s1, s2, n));
111 }
112
113 static char_type* assign(char_type* s, size_t n, char_type c)
114 {
115 /**
116 * Note: Even though memset accepts int as its second argument,
117 * the actual implementation assigns that int to a dereferenced
118 * char pointer.
119 */
120 return static_cast<char_type*>(memset(s, static_cast<int>(c), n));
121 }
122
123 static constexpr int_type not_eof(int_type c) noexcept
124 {
125 if (!eq_int_type(c, eof()))
126 return c;
127 else
128 return to_int_type('a'); // We just need something that is not eof.
129 }
130
131 static constexpr char_type to_char_type(int_type c) noexcept
132 {
133 return static_cast<char_type>(c);
134 }
135
136 static constexpr int_type to_int_type(char_type c) noexcept
137 {
138 return static_cast<int_type>(c);
139 }
140
141 static constexpr bool eq_int_type(int_type c1, int_type c2) noexcept
142 {
143 return c1 == c2;
144 }
145
146 static constexpr int_type eof() noexcept
147 {
148 return static_cast<int_type>(EOF);
149 }
150 };
151
152 template<>
153 struct char_traits<char16_t>
154 { /* TODO: implement */ };
155
156 template<>
157 struct char_traits<char32_t>
158 { /* TODO: implement */ };
159
160 template<>
161 struct char_traits<wchar_t>
162 {
163 using char_type = wchar_t;
164 using int_type = wint_t;
165 using off_type = streamoff;
166 using pos_type = wstreampos;
167 /* using state_type = mbstate_t; */
168
169 static void assign(char_type& c1, char_type& c2) noexcept
170 {
171 c1 = c2;
172 }
173
174 static constexpr bool eq(char_type c1, char_type c2) noexcept
175 {
176 return c1 == c2;
177 }
178
179 static constexpr bool lt(char_type c1, char_type c2) noexcept
180 {
181 return c1 < c2;
182 }
183
184 static int compare(const char_type* s1, const char_type* s2, size_t n)
185 {
186 // TODO: This function does not exits...
187 //return std::wstr_lcmp(s1, s2, n);
188 return 0;
189 }
190
191 static size_t length(const char_type* s)
192 {
193 return std::wstr_size(s) + 1;
194 }
195
196 static const char_type* find(const char_type* s, size_t n, const char_type& c)
197 {
198 size_t i{};
199 while (i++ < n)
200 {
201 if (s[i] == c)
202 return s + i;
203 }
204
205 return nullptr;
206 }
207
208 static char_type* move(char_type* s1, const char_type* s2, size_t n)
209 {
210 return static_cast<char_type*>(memmove(s1, s2, n * sizeof(wchar_t)));
211 }
212
213 static char_type* copy(char_type* s1, const char_type* s2, size_t n)
214 {
215 return static_cast<char_type*>(memcpy(s1, s2, n * sizeof(wchar_t)));
216 }
217
218 static char_type* assign(char_type* s, size_t n, char_type c)
219 {
220 return static_cast<char_type*>(memset(s, static_cast<int>(c), n * sizeof(wchar_t)));
221 }
222
223 static constexpr int_type not_eof(int_type c) noexcept
224 {
225 if (!eq_int_type(c, eof()))
226 return c;
227 else
228 return to_int_type(L'a'); // We just need something that is not eof.
229 }
230
231 static constexpr char_type to_char_type(int_type c) noexcept
232 {
233 return static_cast<char_type>(c);
234 }
235
236 static constexpr int_type to_int_type(char_type c) noexcept
237 {
238 return static_cast<int_type>(c);
239 }
240
241 static constexpr bool eq_int_type(int_type c1, int_type c2) noexcept
242 {
243 return c1 == c2;
244 }
245
246 static constexpr int_type eof() noexcept
247 {
248 return static_cast<int_type>(EOF);
249 }
250 };
251
252 /**
253 * 21.4, class template basic_string:
254 */
255
256 template<class Char, class Traits = char_traits<Char>,
257 class Allocator = allocator<Char>>
258 class basic_string
259 {
260 public:
261 using traits_type = Traits;
262 using value_type = typename traits_type::char_type;
263 using allocator_type = Allocator;
264 using size_type = typename allocator_traits<allocator_type>::size_type;
265 using difference_type = typename allocator_traits<allocator_type>::difference_type;
266
267 using reference = value_type&;
268 using const_reference = const value_type&;
269 using pointer = typename allocator_traits<allocator_type>::pointer;
270 using const_pointer = typename allocator_traits<allocator_type>::const_pointer;
271
272 using iterator = pointer;
273 using const_iterator = const_pointer;
274 using reverse_iterator = std::reverse_iterator<iterator>;
275 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
276
277 static constexpr size_type npos = -1;
278
279 /**
280 * 21.4.2, construct/copy/destroy:
281 */
282 basic_string() noexcept
283 : basic_string(allocator_type{})
284 { /* DUMMY BODY */ }
285
286 explicit basic_string(const allocator_type& alloc)
287 : data_{}, size_{}, capacity_{}, allocator_{alloc}
288 {
289 /**
290 * Postconditions:
291 * data() = non-null copyable value that can have 0 added to it.
292 * size() = 0
293 * capacity() = unspecified
294 */
295 data_ = allocator_.allocate(default_capacity_);
296 capacity_ = default_capacity_;
297 }
298
299 basic_string(const basic_string& other)
300 : data_{}, size_{other.size_}, capacity_{other.capacity_},
301 allocator_{other.allocator_}
302 {
303 init_(other.data(), size_);
304 }
305
306 basic_string(basic_string&& other)
307 : data_{other.data_}, size_{other.size_},
308 capacity_{other.capacity_}, allocator_{move(other.allocator_)}
309 {
310 other.data_ = nullptr;
311 other.size_ = 0;
312 other.capacity_ = 0;
313 }
314
315 basic_string(const basic_string& other, size_type pos, size_type n = npos,
316 const allocator_type& alloc = allocator_type{})
317 : data_{}, size_{}, capacity_{}, allocator_{alloc}
318 {
319 // TODO: if pos < other.size() throw out_of_range.
320 auto len = min(n, other.size() - pos);
321 init_(other.data() + pos, len);
322 }
323
324 basic_string(const value_type* str, size_type n, const allocator_type& alloc = allocator_type{})
325 : data_{}, size_{n}, capacity_{n}, allocator_{alloc}
326 {
327 init_(str, size_);
328 }
329
330 basic_string(const value_type* str, const allocator_type& alloc = allocator_type{})
331 : data_{}, size_{}, capacity_{}, allocator_{alloc}
332 {
333 init_(str, traits_type::length(str));
334 }
335
336 basic_string(size_type n, value_type c, const allocator_type& alloc = allocator_type{})
337 : data_{}, size_{n}, capacity_{n}, allocator_{alloc}
338 {
339 data_ = allocator_.allocate(capacity_);
340 for (size_type i = 0; i < size_; ++i)
341 traits_type::asign(data_[i], c);
342 ensure_null_terminator_();
343 }
344
345 template<class InputIterator>
346 basic_string(InputIterator first, InputIterator last,
347 const allocator_type& alloc = allocator_type{})
348 : data_{}, size_{}, capacity_{}, allocator_{alloc}
349 {
350 if constexpr (is_integral<InputIterator>::value)
351 { // Required by the standard.
352 size_ = static_cast<size_type>(first);
353 capacity_ = size_;
354 data_ = allocator_.allocate(capacity_);
355
356 for (size_type i = 0; i < size_; ++i)
357 traits_type::assign(data_[i], static_cast<value_type>(last));
358 ensure_null_terminator_();
359 }
360 else
361 {
362 auto len = static_cast<size_type>(last - first);
363 init_(static_cast<value_type*>(first), len);
364 }
365 }
366
367 basic_string(initializer_list<value_type> init, const allocator_type& alloc = allocator_type{})
368 : basic_string{init.begin(), init.size(), alloc}
369 { /* DUMMY BODY */ }
370
371 basic_string(const basic_string& other, const allocator_type& alloc)
372 : data_{}, size_{other.size_}, capacity_{other.capacity_}, allocator_{alloc}
373 {
374 init_(other.data(), size_);
375 }
376
377 basic_string(basic_string&& other, const allocator_type& alloc)
378 : data_{other.data_}, size_{other.size_}, capacity_{other.capacity_}, allocator_{alloc}
379 {
380 other.data_ = nullptr;
381 other.size_ = 0;
382 other.capacity_ = 0;
383 }
384
385 ~basic_string()
386 {
387 allocator_.deallocate(data_, capacity_);
388 }
389
390 basic_string& operator=(const basic_string& other)
391 {
392 if (this != &other)
393 {
394 basic_string tmp{other};
395 swap(tmp);
396 }
397 }
398
399 basic_string& operator=(basic_string&& other)
400 noexcept(allocator_traits<allocator_type>::propagate_on_container_move_assignment::value ||
401 allocator_traits<allocator_type>::is_always_equal::value)
402 {
403 if (this != &other)
404 swap(other);
405 }
406
407 basic_string& operator=(const value_type* other)
408 {
409 *this = basic_string{other};
410
411 return *this;
412 }
413
414 basic_string& operator=(value_type c)
415 {
416 *this = basic_string{1, c};
417
418 return *this;
419 }
420
421 basic_string& operator=(initializer_list<value_type> init)
422 {
423 *this = basic_string{init};
424
425 return *this;
426 }
427
428 /**
429 * 21.4.3, iterators:
430 */
431
432 iterator begin() noexcept
433 {
434 return &data_[0];
435 }
436
437 const_iterator begin() const noexcept
438 {
439 return &data_[0];
440 }
441
442 iterator end() noexcept
443 {
444 return begin() + size_;
445 }
446
447 const_iterator end() const noexcept
448 {
449 return begin() + size_;
450 }
451
452 reverse_iterator rbegin() noexcept
453 {
454 return make_reverse_iterator(end());
455 }
456
457 const_reverse_iterator rbegin() const noexcept
458 {
459 return make_reverse_iterator(cend());
460 }
461
462 reverse_iterator rend() noexcept
463 {
464 return make_reverse_iterator(begin());
465 }
466
467 const_reverse_iterator rend() const noexcept
468 {
469 return make_reverse_iterator(cbegin());
470 }
471
472 const_iterator cbegin() const noexcept
473 {
474 return &data_[0];
475 }
476
477 const_iterator cend() const noexcept
478 {
479 return cbegin() + size_;
480 }
481
482 const_reverse_iterator crbegin() const noexcept
483 {
484 return rbegin();
485 }
486
487 const_reverse_iterator crend() const noexcept
488 {
489 return rend();
490 }
491
492 /**
493 * 21.4.4, capacity:
494 */
495
496 size_type size() const noexcept
497 {
498 return size_;
499 }
500
501 size_type length() const noexcept
502 {
503 return size_;
504 }
505
506 size_type max_size() const noexcept
507 {
508 return allocator_traits<allocator_type>::max_size(allocator_);
509 }
510
511 void resize(size_type new_size, value_type c)
512 {
513 // TODO: if new_size > max_size() throw length_error.
514 if (new_size > size_)
515 {
516 ensure_free_space_(new_size - size_ + 1);
517 for (size_type i = size_; i < new_size; ++i)
518 traits_type::assign(data_[i], i);
519 }
520
521 size_ = new_size;
522 ensure_null_terminator_();
523 }
524
525 void resize(size_type new_size)
526 {
527 resize(new_size, value_type{});
528 }
529
530 size_type capacity() const noexcept
531 {
532 return capacity_;
533 }
534
535 void reserve(size_type new_capacity = 0)
536 {
537 // TODO: if new_capacity > max_size() throw
538 // length_error (this function shall have no
539 // effect in such case)
540 if (new_capacity > capacity_)
541 resize_with_copy_(size_, new_capacity);
542 else if (new_capacity < capacity)
543 shrink_to_fit(); // Non-binding request, but why not.
544 }
545
546 void shrink_to_fit()
547 {
548 if (size_ != capacity_)
549 resize_with_copy_(size_);
550 }
551
552 void clear() noexcept
553 {
554 size_ = 0;
555 }
556
557 bool empty() const noexcept
558 {
559 return size_ == 0;
560 }
561
562 /**
563 * 21.4.5, element access:
564 */
565
566 const_reference operator[](size_type idx) const
567 {
568 return data_[idx];
569 }
570
571 reference operator[](size_type idx)
572 {
573 return data_[idx];
574 }
575
576 const_reference at(size_type idx) const
577 {
578 // TODO: bounds checking
579 return data_[idx];
580 }
581
582 reference at(size_type idx)
583 {
584 // TODO: bounds checking
585 return data_[idx];
586 }
587
588 const_reference front() const
589 {
590 return at(0);
591 }
592
593 reference front()
594 {
595 return at(0);
596 }
597
598 const_reference back() const
599 {
600 return at(size_ - 1);
601 }
602
603 reference back()
604 {
605 return at(size_ - 1);
606 }
607
608 /**
609 * 21.4.6, modifiers:
610 */
611
612 basic_string& operator+=(const basic_string& str)
613 {
614 return append(str);
615 }
616
617 basic_string& operator+=(const value_type* str)
618 {
619 return append(str);
620 }
621
622 basic_string& operator+=(value_type c)
623 {
624 push_back(c);
625 return *this;
626 }
627
628 basic_string& operator+=(initializer_list<value_type> init)
629 {
630 return append(init.begin(), init.size());
631 }
632
633 basic_string& append(const basic_string& str)
634 {
635 return append(str.data(), str.size());
636 }
637
638 basic_string& append(const basic_string& str, size_type pos,
639 size_type n = npos)
640 {
641 if (pos < str.size())
642 {
643 auto len = min(n, str.size() - pos);
644 ensure_free_space_(len);
645
646 return append(str.data() + pos, len);
647 }
648 // TODO: Else throw out_of_range.
649 }
650
651 basic_string& append(const value_type* str, size_type n)
652 {
653 // TODO: if (size_ + n > max_size()) throw length_error
654 ensure_free_space_(n);
655 traits_type::copy(data_ + size_, str, n);
656 size_ += n;
657 ensure_null_terminator_();
658
659 return *this;
660 }
661
662 basic_string& append(const value_type* str)
663 {
664 return append(str, traits_type::length(str));
665 }
666
667 basic_string& append(size_type n, value_type c)
668 {
669 return append(basic_string(n, c));
670 }
671
672 template<class InputIterator>
673 basic_string& append(InputIterator first, InputIterator last)
674 {
675 return append(basic_string(first, last));
676 }
677
678 basic_string& append(initializer_list<value_type> init)
679 {
680 return append(init.begin(), init.size());
681 }
682
683 void push_back(value_type c)
684 {
685 ensure_free_space_(1);
686 traits_type::assign(data_[size_++], c);
687 ensure_null_terminator_();
688 }
689
690 basic_string& assign(const basic_string& str)
691 {
692 return assign(str, 0, npos);
693 }
694
695 basic_string& assign(basic_string&& str)
696 {
697 swap(str);
698
699 return *this;
700 }
701
702 basic_string& assign(const basic_string& str, size_type pos,
703 size_type n = npos)
704 {
705 if (pos < str.size())
706 {
707 auto len = min(n, str.size() - pos);
708 ensure_free_space_(len);
709
710 return assign(str.data() + pos, len);
711 }
712 // TODO: Else throw out_of_range.
713
714 return *this;
715 }
716
717 basic_string& assign(const value_type* str, size_type n)
718 {
719 // TODO: if (n > max_size()) throw length_error.
720 resize_without_copy_(n);
721 traits_type::copy(begin(), str, n);
722 size_ = n;
723 ensure_null_terminator_();
724
725 return *this;
726 }
727
728 basic_string& assign(const value_type* str)
729 {
730 return assign(str, traits_type::length(str));
731 }
732
733 basic_string& assign(size_type n, value_type c)
734 {
735 return assign(basic_string(n, c));
736 }
737
738 template<class InputIterator>
739 basic_string& assign(InputIterator first, InputIterator last)
740 {
741 return assign(basic_string(first, last));
742 }
743
744 basic_string& assign(initializer_list<value_type> init)
745 {
746 return assign(init.begin(), init.size());
747 }
748
749 basic_string& insert(size_type pos, const basic_string& str)
750 {
751 // TODO: if (pos > str.size()) throw out_of_range.
752 return insert(pos, str.data(), str.size());
753 }
754
755 basic_string& insert(size_type pos1, const basic_string& str,
756 size_type pos2, size_type n = npos)
757 {
758 // TODO: if (pos1 > size() or pos2 > str.size()) throw
759 // out_of_range.
760 auto len = min(n, str.size() - pos2);
761
762 return insert(pos1, str.data() + pos2, len);
763 }
764
765 basic_string& insert(size_type pos, const value_type* str, size_type n)
766 {
767 ensure_free_space_(size_ + n);
768 copy_backward_(begin() + pos, end(), end() + n);
769 copy_(begin() + pos, begin() + pos + n, str);
770
771 return *this;
772 }
773
774 basic_string& insert(size_type pos, const value_type* str)
775 {
776 return insert(pos, str, traits_type::length(str));
777 }
778
779 basic_string& insert(size_type pos, size_type n, value_type c)
780 {
781 return insert(pos, basic_string(n, c));
782 }
783
784 iterator insert(const_iterator pos, value_type c)
785 {
786 auto idx = static_cast<size_type>(pos - begin());
787
788 ensure_free_space_(1);
789 copy_backward_(begin() + pos, end(), end() + 1);
790 ensure_null_terminator_();
791
792 return begin() + idx;
793 }
794
795 iterator insert(const_iterator pos, size_type n, value_type c)
796 {
797 if (n == 0)
798 return const_cast<iterator>(pos);
799
800 auto idx = static_cast<size_type>(pos - begin());
801
802 ensure_free_space_(n);
803 copy_backward_(begin() + pos, end(), end() + n);
804
805 auto it = pos;
806 for (size_type i = 0; i < n; ++i)
807 traits_type::assign(*it++, c);
808 ensure_null_terminator_();
809
810 return begin() + idx;
811 }
812
813 template<class InputIterator>
814 iterator insert(const_iterator pos, InputIterator first,
815 InputIterator last)
816 {
817 return insert(pos - begin(), basic_string(first, last));
818 }
819
820 iterator insert(const_iterator pos, initializer_list<value_type> init)
821 {
822 return insert(pos, init.begin(), init.end());
823 }
824
825 basic_string& erase(size_type pos = 0, size_type n = npos)
826 {
827 auto len = min(n, size_ - pos);
828 copy_(begin() + pos + n, end(), begin() + pos);
829 size_ -= len;
830 ensure_null_terminator_();
831
832 return *this;
833 }
834
835 iterator erase(const_iterator pos)
836 {
837 auto idx = static_cast<size_type>(pos - cbegin());
838 erase(idx, 1);
839
840 return begin() + idx;
841 }
842
843 iterator erase(const_iterator first, const_iterator last)
844 {
845 auto idx = static_cast<size_type>(first - cbegin());
846 auto count = static_cast<size_type>(last - first);
847 erase(idx, count);
848
849 return begin() + idx;
850 }
851
852 void pop_back()
853 {
854 --size_;
855 ensure_null_terminator_();
856 }
857
858 basic_string& replace(size_type pos, size_type n, const basic_string& str)
859 {
860 // TODO: throw out_of_range if pos > size()
861 return replace(pos, n, str.data(), str.size());
862 }
863
864 basic_string& replace(size_type pos1, size_type n1, const basic_string& str,
865 size_type pos2, size_type n2 = npos)
866 {
867 // TODO: throw out_of_range if pos1 > size() or pos2 > str.size()
868 auto len = min(n2, str.size() - pos2);
869 return replace(pos1, n1, str.data() + pos2, len);
870 }
871
872 basic_string& replace(size_type pos, size_type n1, const value_type* str,
873 size_type n2)
874 {
875 // TODO: format and comment this properly
876 // TODO: throw out_of_range if pos > size()
877 auto len = min(n1, size_ - pos);
878 // TODO: if size() - len > max_size() - n2 throw length_error
879 basic_string tmp{};
880 tmp.resize_without_copy_(size_ - len + n2);
881 copy_(begin(), begin() + pos, tmp.begin());
882 traits_type::copy(tmp.begin() + pos + 1, str, n2);
883 copy_(begin() + pos + len, end(), tmp.begin() + pos + 1 + n2);
884 swap(tmp);
885 return *this;
886 }
887
888 basic_string& replace(size_type pos, size_type n, const value_type* str)
889 {
890 return replace(pos, n, str, traits_type::length(str));
891 }
892
893 basic_string& replace(size_type pos, size_type n1, size_type n2,
894 value_type c)
895 {
896 return replace(pos, n1, basic_string(n2, c));
897 }
898
899 basic_string& replace(const_iterator i1, const_iterator i2,
900 const basic_string& str)
901 {
902 return replace(i1 - begin(), i2 - i1, str);
903 }
904
905 basic_string& replace(const_iterator i1, const_iterator i2,
906 const value_type* str, size_type n)
907 {
908 return replace(i1 - begin(), i2 - i1, str, n);
909 }
910
911 basic_string& replace(const_iterator i1, const_iterator i2,
912 const value_type* str)
913 {
914 return replace(i1 - begin(), i2 - i1, str, traits_type::length(str));
915 }
916
917 basic_string& replace(const_iterator i1, const_iterator i2,
918 size_type n, value_type c)
919 {
920 return replace(i1 - begin(), i2 - i1, basic_string(n, c));
921 }
922
923 template<class InputIterator>
924 basic_string& replace(const_iterator i1, const_iterator i2,
925 InputIterator j1, InputIterator j2)
926 {
927 return replace(i1 - begin(), i2 - i1, basic_string(j1, j2));
928 }
929
930 basic_string& replace(const_iterator i1, const_iterator i2,
931 initializer_list<value_type> init)
932 {
933 return replace(i1 - begin(), i2 - i1, init.begin(), init.size());
934 }
935
936 size_type copy(value_type* str, size_type n, size_type pos = 0) const;
937
938 void swap(basic_string& other)
939 noexcept(allocator_traits<allocator_type>::propagate_on_container_swap::value ||
940 allocator_traits<allocator_type>::is_always_equal)
941 {
942 std::swap(data_, other.data_);
943 std::swap(size_, other.size_);
944 std::swap(capacity_, other.capacity_);
945 }
946
947 /**
948 * 21.4.7, string operations:
949 */
950
951 const value_type* c_str() const noexcept
952 {
953 return data_;
954 }
955
956 const value_type* data() const noexcept
957 {
958 return data_;
959 }
960
961 allocator_type get_allocator() const noexcept
962 {
963 return allocator_type{allocator_};
964 }
965
966 /**
967 * Note: The following find functions have 4 versions each:
968 * (1) takes basic_string
969 * (2) takes c string and length
970 * (3) takes c string
971 * (4) takes value_type
972 * According to the C++14 standard, only (1) is marked as
973 * noexcept and the other three return the first one with
974 * a newly allocated strings (and thus cannot be noexcept).
975 * However, allocating a new string results in memory
976 * allocation and copying of the source and thus we have
977 * decided to follow C++17 signatures of these functions
978 * (i.e. all of them being marked as noexcept) and use
979 * (2) for the actual implementation (and avoiding any
980 * allocations or copying in the process and also providing
981 * stronger guarantees to the user).
982 */
983
984 size_type find(const basic_string& str, size_type pos = 0) const noexcept
985 {
986 return find(str.c_str(), pos, str.size());
987 }
988
989 size_type find(const value_type* str, size_type pos, size_type len) const noexcept
990 {
991 if (empty() || len == 0 || len - pos > size())
992 return npos;
993
994 size_type idx{pos};
995
996 while (idx + len < size_)
997 {
998 if (substr_starts_at_(idx, str, len))
999 return idx;
1000 ++idx;
1001 }
1002
1003 return npos;
1004 }
1005
1006 size_type find(const value_type* str, size_type pos = 0) const noexcept
1007 {
1008 return find(str, pos, traits_type::length(str));
1009 }
1010
1011 size_type find(value_type c, size_type pos = 0) const noexcept
1012 {
1013 if (empty())
1014 return npos;
1015
1016 for (size_type i = pos; i < size_; ++i)
1017 {
1018 if (traits_type::eq(c, data_[i]))
1019 return i;
1020 }
1021
1022 return npos;
1023 }
1024
1025 size_type rfind(const basic_string& str, size_type pos = npos) const noexcept
1026 {
1027 return rfind(str.c_str(), pos, str.size());
1028 }
1029
1030 size_type rfind(const value_type* str, size_type pos, size_type len) const noexcept
1031 {
1032 if (empty() || len == 0 || len - pos > size())
1033 return npos;
1034
1035 size_type idx{min(pos, size_ - 1) + 1};
1036
1037 while (idx > 0)
1038 {
1039 if (substr_starts_at_(idx - 1, str, len))
1040 return idx - 1;
1041 --idx;
1042 }
1043
1044 return npos;
1045 }
1046
1047 size_type rfind(const value_type* str, size_type pos = npos) const noexcept
1048 {
1049 return rfind(str, pos, traits_type::length(str));
1050 }
1051
1052 size_type rfind(value_type c, size_type pos = npos) const noexcept
1053 {
1054 if (empty())
1055 return npos;
1056
1057 for (size_type i = min(pos, size_ - 1) + 1; i > 0; --i)
1058 {
1059 if (traits_type::eq(c, data_[i - 1]))
1060 return i - 1;
1061 }
1062
1063 return npos;
1064 }
1065
1066 size_type find_first_of(const basic_string& str, size_type pos = 0) const noexcept
1067 {
1068 return find_first_of(str.c_str(), pos, str.size());
1069 }
1070
1071 size_type find_first_of(const value_type* str, size_type pos, size_type len) const noexcept
1072 {
1073 if (empty() || len == 0 || pos + len > size())
1074 return npos;
1075
1076 size_type idx{pos};
1077
1078 while (idx < size_)
1079 {
1080 if (is_any_of_(idx, str, len))
1081 return idx;
1082 ++idx;
1083 }
1084
1085 return npos;
1086 }
1087
1088 size_type find_first_of(const value_type* str, size_type pos = 0) const noexcept
1089 {
1090 return find_first_of(str, pos, traits_type::length(str));
1091 }
1092
1093 size_type find_first_of(value_type c, size_type pos = 0) const noexcept
1094 {
1095 return find(c, pos);
1096 }
1097
1098 size_type find_last_of(const basic_string& str, size_type pos = npos) const noexcept
1099 {
1100 return find_last_of(str.c_str(), pos, str.size());
1101 }
1102
1103 size_type find_last_of(const value_type* str, size_type pos, size_type len) const noexcept
1104 {
1105 if (empty())
1106 return npos;
1107
1108 for (size_type i = min(pos, size_ - 1) + 1; i > 0; --i)
1109 {
1110 if (is_any_of_(i - 1, str, len))
1111 return i - 1;
1112 }
1113
1114 return npos;
1115 }
1116
1117 size_type find_last_of(const value_type* str, size_type pos = npos) const noexcept
1118 {
1119 return find_last_of(str, pos, traits_type::length(str));
1120 }
1121
1122 size_type find_last_of(value_type c, size_type pos = npos) const noexcept
1123 {
1124 return rfind(c, pos);
1125 }
1126
1127 size_type find_first_not_of(const basic_string& str, size_type pos = 0) const noexcept
1128 {
1129 return find_first_not_of(str.c_str(), pos, str.size());
1130 }
1131
1132 size_type find_first_not_of(const value_type* str, size_type pos, size_type len) const noexcept
1133 {
1134 if (empty() || len == 0 || pos + len > size())
1135 return npos;
1136
1137 size_type idx{pos};
1138
1139 while (idx < size_)
1140 {
1141 if (!is_any_of_(idx, str, len))
1142 return idx;
1143 ++idx;
1144 }
1145
1146 return npos;
1147 }
1148
1149 size_type find_first_not_of(const value_type* str, size_type pos = 0) const noexcept
1150 {
1151 return find_first_not_of(str.c_str(), pos, str.size());
1152 }
1153
1154 size_type find_first_not_of(value_type c, size_type pos = 0) const noexcept
1155 {
1156 if (empty())
1157 return npos;
1158
1159 for (size_type i = pos; i < size_; ++i)
1160 {
1161 if (!traits_type::eq(c, data_[i]))
1162 return i;
1163 }
1164
1165 return npos;
1166 }
1167
1168 size_type find_last_not_of(const basic_string& str, size_type pos = npos) const noexcept
1169 {
1170 return find_last_not_of(str.c_str(), pos, str.size());
1171 }
1172
1173 size_type find_last_not_of(const value_type* str, size_type pos, size_type len) const noexcept
1174 {
1175 if (empty())
1176 return npos;
1177
1178 for (size_type i = min(pos, size_ - 1) + 1; i > 0; --i)
1179 {
1180 if (!is_one_of_(i - 1, str, len))
1181 return i - 1;
1182 }
1183
1184 return npos;
1185 }
1186
1187 size_type find_last_not_of(const value_type* str, size_type pos = npos) const noexcept
1188 {
1189 return find_last_not_of(str, pos, traits_type::length(str));
1190 }
1191
1192 size_type find_last_not_of(value_type c, size_type pos = npos) const noexcept
1193 {
1194 if (empty())
1195 return npos;
1196
1197 pos = min(pos, size_ - 1);
1198
1199 for (size_type i = min(pos, size_ - 1) + 1; i > 1; --i)
1200 {
1201 if (!traits_type::eq(c, data_[i - 1]))
1202 return i - 1;
1203 }
1204
1205 return npos;
1206 }
1207
1208 basic_string substr(size_type pos = 0, size_type n = npos) const
1209 {
1210 // TODO: throw out_of_range if pos > size().
1211 auto len = min(n, size_ - pos);
1212 return basic_string{data() + pos, len};
1213 }
1214
1215 int compare(const basic_string& other) const noexcept
1216 {
1217 auto len = min(size(), other.size());
1218 auto comp = traits_type::compare(data_, other.data(), len);
1219
1220 if (comp != 0)
1221 return comp;
1222 else if (size() == other.size())
1223 return 0;
1224 else if (size() > other.size())
1225 return 1;
1226 else if (size() < other.size())
1227 return -1;
1228 }
1229
1230 int compare(size_type pos, size_type n, const basic_string& other) const
1231 {
1232 return basic_string{*this, pos, n}.compare(other);
1233 }
1234
1235 int compare(size_type pos1, size_type n1, const basic_string& other,
1236 size_type pos2, size_type n2 = npos) const
1237 {
1238 return basic_string{*this, pos1, n1}.compare(basic_string{other, pos2, n2});
1239 }
1240
1241 int compare(const value_type* other) const
1242 {
1243 return compare(basic_string(other));
1244 }
1245
1246 int compare(size_type pos, size_type n, const value_type* other) const
1247 {
1248 return basic_string{*this, pos, n}.compare(basic_string{other});
1249 }
1250
1251 int compare(size_type pos, size_type n1,
1252 const value_type* other, size_type n2) const
1253 {
1254 return basic_string{*this, pos, n1}.compare(basic_string{other, n2});
1255 }
1256
1257 private:
1258 value_type* data_;
1259 size_type size_;
1260 size_type capacity_;
1261 allocator_type allocator_;
1262
1263 /**
1264 * Arbitrary value, standard just requires
1265 * data() to have some capacity.
1266 * (Well, we could've done data_ = &data_ and
1267 * set capacity to 0, but that would be too cryptic.)
1268 */
1269 static constexpr size_type default_capacity_{4};
1270
1271 void init_(const value_type* str, size_type size)
1272 {
1273 if (data_)
1274 allocator_.deallocate(data_, capacity_);
1275
1276 size_ = size;
1277 capacity_ = size;
1278
1279 data_ = allocator_.allocate(capacity_);
1280 traits_type::copy(data_, str, size);
1281 ensure_null_terminator_();
1282 }
1283
1284 size_type next_capacity_(size_type hint = 0) const noexcept
1285 {
1286 if (hint != 0)
1287 return max(capacity_ * 2, hint);
1288 else
1289 return max(capacity_ * 2, 2ul);
1290 }
1291
1292 void ensure_free_space_(size_type n)
1293 {
1294 /**
1295 * Note: We cannot use reserve like we
1296 * did in vector, because in string
1297 * reserve can cause shrinking.
1298 */
1299 if (size_ + 1 + n > capacity_)
1300 resize_with_copy_(size_, max(size_ + 1 + n, next_capacity_()));
1301 }
1302
1303 void resize_without_copy_(size_type capacity)
1304 {
1305 if (data_)
1306 allocator_.deallocate(data_, capacity_);
1307
1308 data_ = allocator_.allocate(capacity);
1309 size_ = 0;
1310 capacity_ = capacity;
1311 ensure_null_terminator_();
1312 }
1313
1314 void resize_with_copy_(size_type size, size_type capacity)
1315 {
1316 if(capacity_ == 0 || capacity_ < capacity)
1317 {
1318 auto new_data = allocator_.allocate(capacity);
1319
1320 auto to_copy = min(size, size_);
1321 traits_type::move(new_data, data_, to_copy);
1322
1323 std::swap(data_, new_data);
1324
1325 allocator_.deallocate(new_data, capacity_);
1326 }
1327
1328 capacity_ = capacity;
1329 size_ = size;
1330 ensure_null_terminator_();
1331 }
1332
1333 iterator copy_(const_iterator first, const_iterator last,
1334 iterator result)
1335 {
1336 while (first != last)
1337 traits_type::assign(*result++, *first++);
1338
1339 ensure_null_terminator_();
1340 return result;
1341 }
1342
1343 iterator copy_backward_(const_iterator first, const_iterator last,
1344 iterator result)
1345 {
1346 while (last-- != first)
1347 traits_type::assign(*result--, *last);
1348
1349 ensure_null_terminator_();
1350 return result;
1351 }
1352
1353 void ensure_null_terminator_()
1354 {
1355 value_type c{};
1356 traits_type::assign(data_[size_], c);
1357 }
1358
1359 bool is_one_of_(size_type idx, const basic_string& str) const
1360 {
1361 auto cstr = str.c_str();
1362 for (size_type i = 0; i < str.size(); ++i)
1363 {
1364 if (traits_type::eq(data_[idx], cstr[i]))
1365 return true;
1366 }
1367
1368 return false;
1369 }
1370
1371 bool substr_starts_at_(size_type idx, const value_type* str, size_type len) const
1372 {
1373 size_type i{};
1374 for (i = 0; i < len; ++i)
1375 {
1376 if (!traits_type::eq(data_[idx + i], str[i]))
1377 break;
1378 }
1379
1380 return i == len;
1381 }
1382 };
1383
1384 using string = basic_string<char>;
1385}
1386
1387#endif
Note: See TracBrowser for help on using the repository browser.