File Coverage

src/panda/intrusive_chain.h
Criterion Covered Total %
statement 113 113 100.0
branch 165 222 74.3
condition n/a
subroutine n/a
pod n/a
total 278 335 82.9


line stmt bran cond sub pod time code
1             #pragma once
2             #include
3             #include
4             #include
5             #include
6             #include
7             #include
8             #include
9             #include
10              
11             namespace panda {
12              
13 560           template struct IntrusiveChainNode {
14             template friend S intrusive_chain_next (const S&);
15             template friend S intrusive_chain_prev (const S&);
16             template friend void intrusive_chain_next (const S&, const S&);
17             template friend void intrusive_chain_prev (const S&, const S&);
18              
19 840           IntrusiveChainNode () : next(), prev() {}
20              
21             protected:
22             T next;
23             T prev;
24             };
25              
26 894           template T intrusive_chain_next (const T& node) { return node->next; }
27 264           template T intrusive_chain_prev (const T& node) { return node->prev; }
28 575           template void intrusive_chain_next (const T& node, const T& val) { node->next = val; }
29 440           template void intrusive_chain_prev (const T& node, const T& val) { node->prev = val; }
30              
31             /// Iterator is not cyclic, so to work with the standard bidirectional algorithms it uses head and tail pointers alongside with the current one.
32             /// Typical cyclic implementation uses an extra sentinel Node object, which we will definitely do not want to allocate.
33 1656           template struct IntrusiveChainIterator {
34             template friend struct IntrusiveChain;
35             template friend struct IntrusiveChainIterator;
36              
37             using difference_type = ptrdiff_t;
38             using value_type = T;
39             using pointer = T*;
40             using reference = T&;
41             using iterator_category = std::bidirectional_iterator_tag;
42              
43 636           IntrusiveChainIterator (const T& tail, const T& current = T()) : tail(tail), current(current) {}
44            
45             template
46             IntrusiveChainIterator (const IntrusiveChainIterator& o) : tail(o.tail), current(o.current) {}
47              
48 680           T& operator* () { return current; }
49             T* operator-> () { return ¤t; }
50 928           bool operator== (const IntrusiveChainIterator& other) const { return current == other.current; }
51 616           bool operator!= (const IntrusiveChainIterator& other) const { return !(*this == other); }
52              
53 320           IntrusiveChainIterator& operator++ () {
54 320           current = intrusive_chain_next(current);
55 320           return *this;
56             }
57              
58             IntrusiveChainIterator operator++ (int) {
59             IntrusiveChainIterator pos(*this);
60             ++*this;
61             return pos;
62             }
63              
64 12           IntrusiveChainIterator& operator-- () {
65 12 100         if (current) current = intrusive_chain_prev(current);
    100          
    100          
    100          
66 8           else current = tail;
67 12           return *this;
68             }
69              
70             IntrusiveChainIterator operator-- (int) {
71             IntrusiveChainIterator pos(*this);
72             --*this;
73             return pos;
74             }
75              
76 32           IntrusiveChainIterator& operator= (const IntrusiveChainIterator& oth) {
77 16           this->tail = oth.tail;
78 16           this->current = oth.current;
79 32           return *this;
80             }
81              
82             private:
83             T tail;
84             T current; // current=nullptr indicates end()
85             };
86              
87             template struct IntrusiveChain {
88             using value_type = T;
89             using size_type = size_t;
90             using difference_type = std::ptrdiff_t;
91             using reference = T&;
92             using const_reference = const T&;
93             using pointer = T*;
94             using const_pointer = const T*;
95             using iterator = IntrusiveChainIterator;
96             using const_iterator = IntrusiveChainIterator;
97             using reverse_iterator = std::reverse_iterator;
98             using const_reverse_iterator = std::reverse_iterator;
99              
100 84           IntrusiveChain () : head(), tail(), _size() {}
101              
102 72           IntrusiveChain (std::initializer_list il) : IntrusiveChain() {
103 164 100         for (const T& node : il) push_back(node);
    50          
    100          
    50          
    100          
    50          
    100          
    50          
104 36           }
105              
106 112           ~IntrusiveChain () { clear(); }
107              
108             IntrusiveChain (const IntrusiveChain&) = delete;
109             IntrusiveChain& operator= (const IntrusiveChain&) = delete;
110              
111             IntrusiveChain (IntrusiveChain&& other) noexcept {
112             std::swap(head, other.head);
113             std::swap(tail, other.tail);
114             }
115              
116             IntrusiveChain& operator= (IntrusiveChain&& other) noexcept {
117             if (this != &other) {
118             std::swap(head, other.head);
119             std::swap(tail, other.tail);
120             std::swap(_size, other._size);
121             }
122             return *this;
123             }
124              
125 168           void push_back (const T& node) {
126 168 100         if (!_size++) {
    100          
    100          
    100          
127 48           head = tail = node;
128             } else {
129 120           intrusive_chain_next(tail, node);
130 120           intrusive_chain_prev(node, tail);
131 120 50         intrusive_chain_next(node, T());
132 120           tail = node;
133             }
134 42           }
135              
136 16           void push_front (const T& node) {
137 16 100         if (!_size++) {
    100          
    100          
    100          
138 4           head = tail = node;
139             } else {
140 12           intrusive_chain_prev(head, node);
141 12 50         intrusive_chain_prev(node, T());
142 12           intrusive_chain_next(node, head);
143 12           head = node;
144             }
145 4           }
146              
147             /// @return false if empty or the last one
148 24           bool pop_back () {
149 24 100         if (head == tail) {
    100          
    100          
    100          
150 2 50         head = tail = T();
151 4           _size = 0;
152 4           return false;
153             }
154 22           auto removed = tail;
155 20           tail = intrusive_chain_prev(tail);
156 20 50         intrusive_chain_next(tail, T());
157 20 50         intrusive_chain_prev(removed, T());
158 20           --_size;
159 11           return true;
160             }
161              
162             /// @return false if empty or the last one
163 32           bool pop_front () {
164 32 100         if (head == tail) {
    100          
    100          
    100          
165 6 50         head = tail = T();
166 12           _size = 0;
167 12           return false;
168             }
169 26           auto removed = head;
170 20           head = intrusive_chain_next(head);
171 20 50         intrusive_chain_prev(head, T());
172 20 50         intrusive_chain_next(removed, T());
173 20           --_size;
174 13           return true;
175             }
176              
177 28           iterator insert (const T& pos, const T& node) {
178 28 100         if (pos) {
    100          
    100          
    100          
179 24           auto prev = intrusive_chain_prev(pos);
180 16 100         if (prev) {
    100          
181 8           ++_size;
182 8 50         intrusive_chain_prev(node, prev);
183 8 50         intrusive_chain_next(node, pos);
184 8 50         intrusive_chain_next(prev, node);
185 8 50         intrusive_chain_prev(pos, node);
186             }
187 12 50         else push_front(node);
    50          
    50          
188             }
189 12           else push_back(node);
190              
191 28           return iterator(tail, node);
192             }
193              
194 32           iterator insert (const const_iterator& pos, const T& node) { return insert(pos.current, node); }
195              
196 60           iterator erase (const T& pos) {
197 60 100         if (!pos) return end();
    50          
    100          
    50          
    100          
    50          
    100          
    50          
198              
199 82 50         auto prev = intrusive_chain_prev(pos);
200 78 50         auto next = intrusive_chain_next(pos);
201              
202 52 100         if (!prev) {
    100          
203 24 100         if (!next && pos != head) return end(); // element is not in container
    100          
    50          
    100          
    100          
    100          
    50          
    100          
    100          
    100          
    50          
    100          
    100          
    50          
204 20 50         pop_front();
    50          
    50          
    50          
205 20 50         return begin();
    50          
    50          
    50          
206             }
207              
208 28 100         if (!next) {
    100          
    100          
    100          
209 12 50         pop_back();
    50          
    50          
    50          
210 12 50         return end();
    50          
    50          
    50          
211             }
212              
213 16 50         intrusive_chain_next(prev, next);
    50          
214 16 50         intrusive_chain_prev(next, prev);
    50          
215 16 50         intrusive_chain_next(pos, T());
    50          
216 16 50         intrusive_chain_prev(pos, T());
    50          
217 16           --_size;
218              
219 38           return iterator(tail, next);
220             }
221              
222 56           iterator erase (const const_iterator& pos) { return erase(pos.current); }
223              
224 60           void clear () {
225 180 100         for (auto node = head; node;) {
    100          
    100          
    100          
226 180           auto next = intrusive_chain_next(node);
227 120 50         intrusive_chain_next(node, T());
228 120 50         intrusive_chain_prev(node, T());
229 60 50         node = next;
230             }
231 30 50         head = tail = T();
232 60           _size = 0;
233 15           }
234              
235 32           reference front () { return head; }
236 32           reference back () { return tail; }
237              
238             const_reference front () const { return head; }
239             const_reference back () const { return tail; }
240              
241 88           iterator begin () { return iterator(tail, head); }
242 112           iterator end () { return iterator(tail); }
243              
244 280           const_iterator begin () const { return const_iterator(tail, head); }
245 280           const_iterator end () const { return const_iterator(tail); }
246            
247             const_iterator cbegin () const { return const_iterator(tail, head); }
248             const_iterator cend () const { return const_iterator(tail); }
249              
250 16           bool empty () const { return !head; }
251              
252 312           size_t size () const { return _size; }
253              
254             private:
255             T head;
256             T tail;
257             size_t _size;
258             };
259              
260             template
261             std::basic_ostream& operator<< (std::basic_ostream& out, const IntrusiveChain& chain) {
262             for (auto node : chain) {
263             out << "node: ";
264             if (node) {
265             out << *node;
266             } else {
267             out << "null";
268             }
269             out << '\n';
270             }
271             return out;
272             }
273              
274             }