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

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

cpp: added basic implementation of all remaining string functions, though most of them still need to be tested

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