1 /*
2    Copyright 2016 Skytechnology sp. z o.o.
3 
4    This file is part of LizardFS.
5 
6    LizardFS is free software: you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation, version 3.
9 
10    LizardFS is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13    GNU General Public License for more details.
14 
15    You should have received a copy of the GNU General Public License
16    along with LizardFS. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #pragma once
20 
21 #include "common/platform.h"
22 
23 #include <algorithm>
24 #include <cassert>
25 #include <iterator>
26 #include <type_traits>
27 
28 /*! \brief Hook class for intrusive list.
29  *
30  * Derive a class from this hook in order to store objects of that class in an intrusive list.
31  */
32 class intrusive_list_base_hook {
33 public:
intrusive_list_base_hook()34 	intrusive_list_base_hook() : prev_node_(nullptr), next_node_(nullptr) {
35 	}
36 
37 	template <class H>
38 	friend class intrusive_list;
39 	template <class H>
40 	friend class intrusive_list_iterator;
41 
42 protected:
43 	intrusive_list_base_hook *prev_node_;
44 	intrusive_list_base_hook *next_node_;
45 };
46 
47 /*! \brief Iterator class for double linked list.
48  */
49 template <typename Node>
50 class intrusive_list_iterator {
51 public:
52 	typedef Node *iterator_type;
53 	typedef std::bidirectional_iterator_tag iterator_category;
54 	typedef Node value_type;
55 	typedef Node& reference;
56 	typedef Node* pointer;
57 	typedef std::ptrdiff_t difference_type;
58 
intrusive_list_iterator()59 	constexpr intrusive_list_iterator() noexcept : current_(nullptr) {
60 	}
61 
intrusive_list_iterator(Node * ptr)62 	explicit intrusive_list_iterator(Node *ptr) noexcept : current_(ptr) {
63 	}
64 
65 	// Conversion from iterator to const_iterator
66 	template <typename OtherNode>
intrusive_list_iterator(const intrusive_list_iterator<typename std::enable_if<std::is_const<OtherNode>::value||!std::is_const<Node>::value,OtherNode>::type> & other)67 	intrusive_list_iterator(
68 	        const intrusive_list_iterator<typename std::enable_if<
69 	                std::is_const<OtherNode>::value || !std::is_const<Node>::value,
70 	                OtherNode>::type> &other) noexcept
71 	        : current_(const_cast<Node *>(other.current_)) {
72 	}
73 
74 	reference operator*() const noexcept {
75 		return *current_;
76 	}
77 
78 	pointer operator->() const noexcept {
79 		return current_;
80 	}
81 
82 	intrusive_list_iterator &operator++() noexcept {
83 		current_ = static_cast<Node *>(current_->next_node_);
84 		return *this;
85 	}
86 
87 	intrusive_list_iterator operator++(int)noexcept {
88 		Node *tmp = current_;
89 		current_ = static_cast<Node *>(current_->next_node_);
90 		return intrusive_list_iterator(tmp);
91 	}
92 
93 	intrusive_list_iterator &operator--() noexcept {
94 		current_ = static_cast<Node *>(current_->prev_node_);
95 		return *this;
96 	}
97 
98 	intrusive_list_iterator operator--(int)noexcept {
99 		Node *tmp = current_;
100 		current_ = static_cast<Node *>(current_->prev_node_);
101 		return intrusive_list_iterator(tmp);
102 	}
103 
current()104 	Node *current() const {
105 		return current_;
106 	}
107 
108 protected:
109 	Node *current_;
110 };
111 
112 template <typename NodeL, typename NodeR>
113 inline bool operator==(const intrusive_list_iterator<NodeL> &lhs,
114 	const intrusive_list_iterator<NodeR> &rhs) noexcept {
115 	return lhs.current() == rhs.current();
116 }
117 
118 template <typename Node>
119 inline bool operator==(const intrusive_list_iterator<Node> &lhs,
120 	const intrusive_list_iterator<Node> &rhs) noexcept {
121 	return lhs.current() == rhs.current();
122 }
123 
124 template <typename NodeL, typename NodeR>
125 inline bool operator!=(const intrusive_list_iterator<NodeL> &lhs,
126 	const intrusive_list_iterator<NodeR> &rhs) noexcept {
127 	return lhs.current() != rhs.current();
128 }
129 
130 template <typename Node>
131 inline bool operator!=(const intrusive_list_iterator<Node> &lhs,
132 	const intrusive_list_iterator<Node> &rhs) noexcept {
133 	return lhs.current() != rhs.current();
134 }
135 
136 template <typename NodeL, typename NodeR>
137 inline bool operator<(const intrusive_list_iterator<NodeL> &lhs,
138 	const intrusive_list_iterator<NodeR> &rhs) noexcept {
139 	return lhs.current() < rhs.current();
140 }
141 
142 template <typename Node>
143 inline bool operator<(const intrusive_list_iterator<Node> &lhs,
144 	const intrusive_list_iterator<Node> &rhs) noexcept {
145 	return lhs.current() < rhs.current();
146 }
147 
148 template <typename NodeL, typename NodeR>
149 inline bool operator>(const intrusive_list_iterator<NodeL> &lhs,
150 	const intrusive_list_iterator<NodeR> &rhs) noexcept {
151 	return lhs.current() > rhs.current();
152 }
153 
154 template <typename Node>
155 inline bool operator>(const intrusive_list_iterator<Node> &lhs,
156 	const intrusive_list_iterator<Node> &rhs) noexcept {
157 	return lhs.current() > rhs.current();
158 }
159 
160 template <typename NodeL, typename NodeR>
161 inline bool operator<=(const intrusive_list_iterator<NodeL> &lhs,
162 	const intrusive_list_iterator<NodeR> &rhs) noexcept {
163 	return lhs.current() <= rhs.current();
164 }
165 
166 template <typename Node>
167 inline bool operator<=(const intrusive_list_iterator<Node> &lhs,
168 	const intrusive_list_iterator<Node> &rhs) noexcept {
169 	return lhs.current() <= rhs.current();
170 }
171 
172 template <typename NodeL, typename NodeR>
173 inline bool operator>=(const intrusive_list_iterator<NodeL> &lhs,
174 	const intrusive_list_iterator<NodeR> &rhs) noexcept {
175 	return lhs.current() >= rhs.current();
176 }
177 
178 template <typename Node>
179 inline bool operator>=(const intrusive_list_iterator<Node> &lhs,
180 	const intrusive_list_iterator<Node> &rhs) noexcept {
181 	return lhs.current() >= rhs.current();
182 }
183 
184 /*! \brief Intrusive list.
185  *
186  * Class used for storing elements derived from intrusive_list_base_hook in a double linked list.
187  * Compared to std::list intrusive list doesn't take ownership of stored elements,
188  * so care must be taken to delete elements after removing them from the list.
189  */
190 template <typename Node>
191 class intrusive_list {
192 public:
193 	typedef Node value_type;
194 	typedef Node& reference;
195 	typedef const Node& const_reference;
196 	typedef Node* pointer;
197 	typedef const Node* const_pointer;
198 	typedef intrusive_list_iterator<Node> iterator;
199 	typedef intrusive_list_iterator<const Node> const_iterator;
200 	typedef std::size_t size_type;
201 
202 public:
intrusive_list()203 	intrusive_list() noexcept : front_(), back_(), size_() {
204 	}
205 
206 	intrusive_list(const intrusive_list &) = delete;
intrusive_list(intrusive_list && other)207 	intrusive_list(intrusive_list &&other) noexcept : front_(), back_(), size_() {
208 		swap(other);
209 	}
210 
211 	intrusive_list &operator=(const intrusive_list &) = delete;
212 	intrusive_list &operator=(intrusive_list &&other) noexcept {
213 		swap(other);
214 		return *this;
215 	}
216 
217 	/*! \brief Destructor.
218 	 *
219 	 * Note that destructor doesn't delete/destroy elements stored in list.
220 	 */
~intrusive_list()221 	~intrusive_list() {
222 	}
223 
front()224 	reference front() {
225 		assert(!empty());
226 		return *front_;
227 	}
228 
back()229 	reference back() {
230 		assert(!empty());
231 		return *back_;
232 	}
233 
front()234 	const_reference front() const {
235 		assert(!empty());
236 		return *front_;
237 	}
238 
back()239 	const_reference back() const {
240 		assert(!empty());
241 		return *back_;
242 	}
243 
pop_front()244 	void pop_front() {
245 		assert(front_ && back_);
246 		Node *tmp = front_;
247 		front_ = static_cast<Node *>(front_->next_node_);
248 		if (!front_) {
249 			back_ = nullptr;
250 		} else {
251 			front_->prev_node_ = nullptr;
252 		}
253 		tmp->prev_node_ = nullptr;
254 		tmp->next_node_ = nullptr;
255 		--size_;
256 	}
257 
pop_back()258 	void pop_back() {
259 		assert(front_ && back_);
260 		Node *tmp = back_;
261 		back_ = static_cast<Node*>(back_->prev_node_);
262 		if (!back_) {
263 			assert(front_ == tmp);
264 			front_ = nullptr;
265 		} else {
266 			back_->next_node_ = nullptr;
267 		}
268 		tmp->prev_node_ = nullptr;
269 		tmp->next_node_ = nullptr;
270 		--size_;
271 	}
272 
273 	/*! \brief Remove and destroy first element from list.
274 	 *
275 	 * \param disposer Function object used for destroying elements removed from list.
276 	 */
277 	template<typename Disposer>
pop_front_and_dispose(Disposer disposer)278 	void pop_front_and_dispose(Disposer disposer) {
279 		Node *tmp = front_;
280 		pop_front();
281 		disposer(tmp);
282 	}
283 
284 	/*! \brief Remove and destroy last element from list.
285 	 *
286 	 * \param disposer Function object used for destroying elements removed from list.
287 	 */
288 	template<typename Disposer>
pop_back_and_dispose(Disposer disposer)289 	void pop_back_and_dispose(Disposer disposer) {
290 		Node *tmp = back_;
291 		pop_back();
292 		disposer(tmp);
293 	}
294 
push_back(value_type & h)295 	void push_back(value_type &h) {
296 		h.next_node_ = nullptr;
297 		h.prev_node_ = back_;
298 		if (back_) {
299 			back_->next_node_ = std::addressof(h);
300 			back_ = std::addressof(h);
301 		} else {
302 			front_ = back_ = std::addressof(h);
303 		}
304 		++size_;
305 	}
306 
push_front(value_type & h)307 	void push_front(value_type &h) {
308 		h.next_node_ = front_;
309 		h.prev_node_ = nullptr;
310 		if (front_) {
311 			front_->prev_node_ = std::addressof(h);
312 			front_ = std::addressof(h);
313 		} else {
314 			front_ = back_ = std::addressof(h);
315 		}
316 		++size_;
317 	}
318 
clear()319 	void clear() {
320 		while (front_) {
321 			Node *tmp = front_;
322 			front_ = static_cast<Node *>(front_->next_node_);
323 			tmp->prev_node_ = nullptr;
324 			tmp->next_node_ = nullptr;
325 		}
326 		back_ = nullptr;
327 		size_ = 0;
328 	}
329 
330 	/*! \brief Remove and destroy all elements from list.
331 	 *
332 	 * \param disposer Function object used for destroying elements removed from list.
333 	 */
334 	template<typename Disposer>
clear_and_dispose(Disposer disposer)335 	void clear_and_dispose(Disposer disposer) {
336 		while (front_) {
337 			Node *tmp = front_;
338 			front_ = static_cast<Node *>(front_->next_node_);
339 			tmp->prev_node_ = nullptr;
340 			tmp->next_node_ = nullptr;
341 			disposer(tmp);
342 		}
343 		back_ = nullptr;
344 		size_ = 0;
345 	}
346 
erase(iterator position)347 	iterator erase(iterator position) {
348 		Node *node = position.current();
349 
350 		if (!node) {
351 			return end();
352 		}
353 
354 		Node *prev = static_cast<Node*>(node->prev_node_);
355 		Node *next = static_cast<Node*>(node->next_node_);
356 
357 		if (prev) {
358 			prev->next_node_ = next;
359 		} else {
360 			front_ = next;
361 		}
362 		if (next) {
363 			next->prev_node_ = prev;
364 		} else {
365 			back_ = prev;
366 		}
367 		--size_;
368 
369 		node->prev_node_ = nullptr;
370 		node->next_node_ = nullptr;
371 
372 		return iterator(next);
373 	}
374 
375 	/*! \brief Remove and destroy single element.
376 	 *
377 	 * \param position Iterator pointing to a single element to be removed from the list.
378 	 * \param disposer Function object used for destroying elements removed from list.
379 	 */
380 	template<typename Disposer>
erase_and_dispose(iterator position,Disposer disposer)381 	iterator erase_and_dispose(iterator position, Disposer disposer) {
382 		Node *node = position.current();
383 		auto it = erase(position);
384 		if (node) {
385 			disposer(node);
386 		}
387 		return it;
388 	}
389 
insert(iterator position,value_type & node)390 	iterator insert(iterator position, value_type &node) {
391 		Node *next = position.current();
392 		Node *prev = next ? static_cast<Node*>(next->prev_node_) : nullptr;
393 
394 		if (prev) {
395 			prev->next_node_ = std::addressof(node);
396 		} else {
397 			front_ = std::addressof(node);
398 		}
399 		if (next) {
400 			next->prev_node_ = std::addressof(node);
401 		} else {
402 			back_ = std::addressof(node);
403 		}
404 		++size_;
405 
406 		node.prev_node_ = prev;
407 		node.next_node_ = next;
408 
409 		return iterator(std::addressof(node));
410 	}
411 
empty()412 	bool empty() const {
413 		return front_ == nullptr;
414 	}
415 
size()416 	size_type size() const {
417 		return size_;
418 	}
419 
swap(intrusive_list & other)420 	void swap(intrusive_list &other) noexcept {
421 		std::swap(front_, other.front_);
422 		std::swap(back_, other.back_);
423 		std::swap(size_, other.size_);
424 	}
425 
begin()426 	iterator begin() {
427 		return iterator(front_);
428 	}
429 
end()430 	iterator end() {
431 		return iterator(nullptr);
432 	}
433 
begin()434 	const_iterator begin() const {
435 		return const_iterator(front_);
436 	}
437 
end()438 	const_iterator end() const {
439 		return const_iterator(nullptr);
440 	}
441 
cbegin()442 	const_iterator cbegin() const {
443 		return const_iterator(front_);
444 	}
445 
cend()446 	const_iterator cend() const {
447 		return const_iterator(nullptr);
448 	}
449 
splice(iterator position,intrusive_list & x)450 	void splice(iterator position, intrusive_list &x) {
451 		if (x.empty()) {
452 			return;
453 		}
454 
455 		Node *node_next = position.current();
456 		Node *node_prev = node_next ? static_cast<Node*>(node_next->prev_node_) : back_;
457 
458 		if (node_prev) {
459 			node_prev->next_node_ = x.front_;
460 		} else {
461 			front_ = x.front_;
462 		}
463 
464 		x.front_->prev_node_ = node_prev;
465 		x.back_->next_node_ = node_next;
466 
467 		if (node_next) {
468 			node_next->prev_node_ = x.back_;
469 		} else {
470 			back_ = x.back_;
471 		}
472 
473 		size_ += x.size_;
474 
475 		x.front_ = nullptr;
476 		x.back_ = nullptr;
477 		x.size_ = 0;
478 	}
479 
480 private:
481 	Node *front_;
482 	Node *back_;
483 	size_type size_;
484 };
485