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