source: mainline/uspace/lib/cpp/include/impl/list.hpp@ 8733ce2a

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

cpp: added list iterators

  • Property mode set to 100644
File size: 20.1 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_LIST
30#define LIBCPP_LIST
31
32#include <cstdlib>
33#include <iterator>
34#include <memory>
35#include <utility>
36
37namespace std
38{
39 template<class T, class Allocator = allocator<T>>
40 class list;
41
42 namespace aux
43 {
44 template<class T>
45 struct list_node
46 {
47 T value;
48 list_node* next;
49 list_node* prev;
50
51 template<class... Args>
52 list_node(Args&&... args)
53 : value{forward<Args>(args)...},
54 next{this}, prev{this}
55 {
56 next = this;
57 prev = this;
58 }
59
60 list_node(const T& val)
61 : value{val}, next{this}, prev{this}
62 {
63 next = this;
64 prev = this;
65 }
66
67 list_node(T&& val)
68 : value{forward<T>(val)}, next{}, prev{}
69 {
70 next = this;
71 prev = this;
72 }
73
74 void append(list_node* node)
75 {
76 node->next = next;
77 node->prev = this;
78 next->prev = node;
79 next = node;
80 }
81
82 void prepend(list_node* node)
83 {
84 node->next = this;
85 node->prev = prev;
86 prev->next = node;
87 prev = node;
88 }
89 };
90
91 template<class T>
92 class list_const_iterator
93 {
94 public:
95 using value_type = typename list<T>::value_type;
96 using reference = typename list<T>::const_reference;
97
98 using iterator_category = forward_iterator_tag;
99
100 list_const_iterator(list_node<value_type>* node = nullptr,
101 list_node<value_type>* head = nullptr)
102 : current_{node}, head_{head}
103 { /* DUMMY BODY */ }
104
105 list_const_iterator(const list_const_iterator&) = default;
106 list_const_iterator& operator=(const list_const_iterator&) = default;
107 list_const_iterator(list_const_iterator&&) = default;
108 list_const_iterator& operator=(list_const_iterator&&) = default;
109
110 reference operator*() const
111 {
112 return current_->value;
113 }
114
115 list_const_iterator& operator++()
116 {
117 if (current_)
118 {
119 if (current_->next == head_)
120 current_ = nullptr;
121 else
122 current_ = current_->next;
123 }
124
125 return *this;
126 }
127
128 list_const_iterator operator++(int)
129 {
130 auto bckp = current_;
131
132 if (current_)
133 {
134 if (current_->next == head_)
135 current_ = nullptr;
136 else
137 current_ = current_->next;
138 }
139
140 return list_const_iterator{bckp};
141 }
142
143 list_node<value_type>* node()
144 {
145 return current_;
146 }
147
148 const list_node<value_type>* node() const
149 {
150 return current_;
151 }
152
153 private:
154 list_node<value_type>* current_;
155 list_node<value_type>* head_;
156 };
157
158 template<class T>
159 bool operator==(const list_const_iterator<T>& lhs, const list_const_iterator<T>& rhs)
160 {
161 return lhs.node() == rhs.node();
162 }
163
164 template<class T>
165 bool operator!=(const list_const_iterator<T>& lhs, const list_const_iterator<T>& rhs)
166 {
167 return !(lhs == rhs);
168 }
169
170 template<class T>
171 class list_iterator
172 {
173 public:
174 using value_type = typename list<T>::value_type;
175 using reference = typename list<T>::reference;
176
177 using iterator_category = forward_iterator_tag;
178
179 list_iterator(list_node<value_type>* node = nullptr,
180 list_node<value_type>* head = nullptr)
181 : current_{node}, head_{head}
182 { /* DUMMY BODY */ }
183
184 list_iterator(const list_iterator&) = default;
185 list_iterator& operator=(const list_iterator&) = default;
186 list_iterator(list_iterator&&) = default;
187 list_iterator& operator=(list_iterator&&) = default;
188
189 reference operator*()
190 {
191 return current_->value;
192 }
193
194 list_iterator& operator++()
195 {
196 if (current_)
197 {
198 if (current_->next == head_)
199 current_ = nullptr;
200 else
201 current_ = current_->next;
202 }
203
204 return *this;
205 }
206
207 list_iterator operator++(int)
208 {
209 auto bckp = current_;
210
211 if (current_)
212 {
213 if (current_->next == head_)
214 current_ = nullptr;
215 else
216 current_ = current_->next;
217 }
218
219 return list_iterator{bckp};
220 }
221
222 list_node<value_type>* node()
223 {
224 return current_;
225 }
226
227 const list_node<value_type>* node() const
228 {
229 return current_;
230 }
231
232 operator list_const_iterator<T>() const
233 {
234 return list_const_iterator{current_};
235 }
236
237 private:
238 list_node<T>* current_;
239 list_node<value_type>* head_;
240 };
241
242 template<class T>
243 bool operator==(const list_iterator<T>& lhs, const list_iterator<T>& rhs)
244 {
245 return lhs.node() == rhs.node();
246 }
247
248 template<class T>
249 bool operator!=(const list_iterator<T>& lhs, const list_iterator<T>& rhs)
250 {
251 return !(lhs == rhs);
252 }
253 }
254
255 /**
256 * 23.3.5, class template list:
257 */
258
259 template<class T, class Allocator>
260 class list
261 {
262 public:
263 using value_type = T;
264 using reference = value_type&;
265 using const_reference = const value_type&;
266 using allocator_type = Allocator;
267
268 using iterator = aux::list_iterator<value_type>;
269 using const_iterator = aux::list_const_iterator<value_type>;
270 using size_type = size_t;
271 using difference_type = ptrdiff_t;
272
273 using pointer = typename allocator_traits<allocator_type>::pointer;
274 using const_pointer = typename allocator_traits<allocator_type>::const_pointer;
275
276 using reverse_iterator = std::reverse_iterator<iterator>;
277 using const_reverse_iterator = std::reverse_iterator<const_iterator>;
278
279 /**
280 * 23.3.5.2, construct/copy/destroy:
281 */
282
283 list()
284 : list{allocator_type{}}
285 { /* DUMMY BODY */ }
286
287 explicit list(const allocator_type& alloc)
288 : allocator_{alloc}, head_{nullptr}, size_{}
289 { /* DUMMY BODY */ }
290
291 explicit list(size_type n, const allocator_type& alloc = allocator_type{})
292 : allocator_{alloc}, head_{nullptr}, size_{}
293 {
294 init_(
295 aux::insert_iterator<value_type>{value_type{}},
296 aux::insert_iterator<value_type>{size_}
297 );
298 }
299
300 list(size_type n, const value_type& val,
301 const allocator_type& alloc = allocator_type{})
302 : allocator_{alloc}, head_{nullptr}, size_{}
303 {
304 init_(
305 aux::insert_iterator<value_type>{val},
306 aux::insert_iterator<value_type>{n}
307 );
308 }
309
310 template<class InputIterator>
311 list(InputIterator first, InputIterator last,
312 const allocator_type& alloc = allocator_type{})
313 : allocator_{alloc}, head_{nullptr}, size_{}
314 {
315 init_(first, last);
316 }
317
318 list(const list& other)
319 : list{other, other.allocator_}
320 { /* DUMMY BODY */ }
321
322 list(list&& other)
323 : allocator_{move(other.allocator_)},
324 head_{move(other.head_)},
325 size_{move(other.size_)}
326 {
327 other.head_ = nullptr;
328 }
329
330 list(const list& other, const allocator_type alloc)
331 : allocator_{alloc}, head_{nullptr}, size_{other.size_}
332 {
333 init_(other.begin(), other.end());
334 }
335
336 list(list&& other, const allocator_type& alloc)
337 : allocator_{alloc},
338 head_{move(other.head_)},
339 size_{move(other.size_)}
340 {
341 other.head_ = nullptr;
342 }
343
344 list(initializer_list<value_type> init, const allocator_type& alloc)
345 : allocator_{alloc}, head_{nullptr}, size_{}
346 {
347 init_(init.begin(), init.end());
348 }
349
350 ~list()
351 {
352 fini_();
353 }
354
355 list& operator=(const list& other)
356 {
357 fini_();
358
359 allocator_ = other.allocator_;
360
361 init_(other.begin(), other.end());
362 }
363
364 list& operator=(list&& other)
365 noexcept(allocator_traits<allocator_type>::is_always_equal::value)
366 {
367 fini_();
368
369 allocator_ = move(other.allocator_);
370
371 init_(
372 make_move_iterator(other.begin()),
373 make_move_iterator(other.end())
374 );
375 }
376
377 list& operator=(initializer_list<value_type> init)
378 {
379 fini_();
380
381 init_(init.begin(), init.end());
382 }
383
384 template<class InputIterator>
385 void assign(InputIterator first, InputIterator last)
386 {
387 fini_();
388
389 init_(first, last);
390 }
391
392 void assign(size_type n, const value_type& val)
393 {
394 fini_();
395
396 init_(
397 aux::insert_iterator<value_type>{val},
398 aux::insert_iterator<value_type>{n}
399 );
400 }
401
402 void assign(initializer_list<value_type> init)
403 {
404 fini_();
405
406 init_(init.begin(), init.end());
407 }
408
409 allocator_type get_allocator() const noexcept
410 {
411 return allocator_;
412 }
413
414 iterator begin() noexcept
415 {
416 return iterator{head_, head_};
417 }
418
419 const_iterator begin() const noexcept
420 {
421 return cbegin();
422 }
423
424 iterator end() noexcept
425 {
426 return iterator{nullptr, head_};
427 }
428
429 const_iterator end() const noexcept
430 {
431 return cend();
432 }
433
434 reverse_iterator rbegin() noexcept
435 {
436 return make_reverse_iterator(end());
437 }
438
439 const_reverse_iterator rbegin() const noexcept
440 {
441 return make_reverse_iterator(cend());
442 }
443
444 reverse_iterator rend() noexcept
445 {
446 return make_reverse_iterator(begin());
447 }
448
449 const_reverse_iterator rend() const noexcept
450 {
451 return make_reverse_iterator(cbegin());
452 }
453
454 const_iterator cbegin() const noexcept
455 {
456 return const_iterator{head_, head_};
457 }
458
459 const_iterator cend() const noexcept
460 {
461 return const_iterator{nullptr, head_};
462 }
463
464 const_reverse_iterator crbegin() const noexcept
465 {
466 return rbegin();
467 }
468
469 /**
470 * 23.3.5.3, capacity:
471 */
472
473 bool empty() const noexcept
474 {
475 return size_ == 0;
476 }
477
478 size_type size() const noexcept
479 {
480 return size_;
481 }
482
483 size_type max_size() const noexcept
484 {
485 return allocator_traits<allocator_type>::max_size(allocator_);
486 }
487
488 void resize(size_type sz)
489 {
490 // TODO: implement
491 }
492
493 void resize(size_type sz, const value_type& val)
494 {
495 // TODO: implement
496 }
497
498 reference front()
499 {
500 // TODO: bounds checking
501 return head_->value;
502 }
503
504 const_reference front() const
505 {
506 // TODO: bounds checking
507 return head_->value;
508 }
509
510 reference back()
511 {
512 // TODO: bounds checking
513 return head_->prev->value;
514 }
515
516 const_reference back() const
517 {
518 // TODO: bounds checking
519 return head_->prev->value;
520 }
521
522 /**
523 * 23.3.5.4, modifiers:
524 * Note: These should have no effect when an exception
525 * is thrown while inserting, but since the only
526 * functions that can throw are the constructors,
527 * creating the node before any modifications to the
528 * list itself will satisfy this requirement.
529 */
530
531 template<class... Args>
532 void emplace_front(Args&&... args)
533 {
534 prepend_new_(forward<Args>(args)...);
535 }
536
537 void pop_front()
538 {
539 if (head_)
540 {
541 --size_;
542
543 if (head_->next == head_)
544 {
545 delete head_;
546 head_ = nullptr;
547 }
548 else
549 {
550 auto tmp = head_;
551 head_->prev->next = head_->next;
552 head_->next->prev = head_->prev;
553 head_ = head_->next;
554
555 delete tmp;
556 }
557 }
558 }
559
560 template<class... Args>
561 void emplace_back(Args&&... args)
562 {
563 append_new_(forward<Args>(args)...);
564 }
565
566 void push_front(const value_type& value)
567 {
568 prepend_new_(value);
569 }
570
571 void push_front(value_type&& value)
572 {
573 prepend_new_(forward<value_type>(value));
574 }
575
576 void push_back(const value_type& value)
577 {
578 append_new_(value);
579 }
580
581 void push_back(value_type&& value)
582 {
583 append_new_(forward<value_type>(value));
584 }
585
586 void pop_back()
587 {
588 if (head_)
589 {
590 --size_;
591 auto target = head_->prev;
592
593 if (!target)
594 {
595 delete head_;
596 head_ = nullptr;
597 }
598 else
599 {
600 auto tmp = target;
601 target->prev->next = target->next;
602 target->next->prev = target->prev;
603 target = target->next;
604
605 delete tmp;
606 }
607 }
608 }
609
610 template<class... Args>
611 iterator emplace(const_iterator position, Args&&... args)
612 {
613 // TODO: implement
614 }
615
616 /* private: */
617 allocator_type allocator_;
618 aux::list_node<value_type>* head_;
619 size_type size_;
620
621 template<class InputIterator>
622 void init_(InputIterator first, InputIterator last)
623 {
624 while (first != last)
625 {
626 auto node = append_new_();
627 allocator_.construct(&node->value, *first++);
628 }
629 }
630
631 void fini_()
632 {
633 if (!head_)
634 return;
635
636 for (size_type i = size_type{}; i < size_; ++i)
637 {
638 head_ = head_->next;
639 delete head_->prev;
640 }
641
642 head_ = nullptr;
643 size_ = size_type{};
644 }
645
646 template<class... Args>
647 aux::list_node<value_type>* append_new_(Args&&... args)
648 {
649 auto node = new aux::list_node<value_type>{forward<Args>(args)...};
650 auto last = get_last_();
651
652 if (!last)
653 head_ = node;
654 else
655 last->append(node);
656
657 ++size_;
658
659 return node;
660 }
661
662 template<class... Args>
663 aux::list_node<value_type>* prepend_new_(Args&&... args)
664 {
665 auto node = new aux::list_node<value_type>{forward<Args>(args)...};
666
667 if (!head_)
668 head_ = node;
669 else
670 {
671 head_->prepend(node);
672 head_ = head_->prev;
673 }
674
675 ++size_;
676
677 return node;
678 }
679
680 aux::list_node<value_type>* get_last_()
681 {
682 if (!head_)
683 return nullptr;
684
685 return head_->prev;
686 }
687
688 template<class InputIterator>
689 void insert_range_(InputIterator first, InputIterator last,
690 aux::list_node<value_type>* where = nullptr)
691 {
692 if (first == last)
693 return;
694
695 if (!where)
696 where = get_last_();
697
698 while (first != last)
699 {
700 where->append(new aux::list_node<value_type>{*first++});
701 where = where->next;
702 }
703 }
704 };
705}
706
707#endif
Note: See TracBrowser for help on using the repository browser.