/* * Copyright 2017 WebAssembly Community Group participants * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #ifndef WABT_INTRUSIVE_LIST_H_ #define WABT_INTRUSIVE_LIST_H_ #include #include #include // This uses a similar interface as std::list, but is missing the following // features: // // * Add "extract_" functions that remove an element from the list and return // it. // * Only supports move-only operations // * No allocator support // * No initializer lists // * Asserts instead of exceptions // * Some functions are not implemented (merge, remove, remove_if, reverse, // unique, sort, non-member comparison operators) namespace wabt { template class intrusive_list; template class intrusive_list_base { private: friend class intrusive_list; mutable T* next_ = nullptr; mutable T* prev_ = nullptr; }; template class intrusive_list { public: // types: using value_type = T; using reference = value_type&; using const_reference = const value_type&; class iterator; class const_iterator; using size_type = std::size_t; using difference_type = std::ptrdiff_t; using reverse_iterator = std::reverse_iterator; using const_reverse_iterator = std::reverse_iterator; // construct/copy/destroy: intrusive_list(); explicit intrusive_list(std::unique_ptr node); explicit intrusive_list(T&& node); intrusive_list(const intrusive_list&) = delete; intrusive_list(intrusive_list&&); ~intrusive_list(); intrusive_list& operator=(const intrusive_list& other) = delete; intrusive_list& operator=(intrusive_list&& other); // iterators: iterator begin() noexcept; const_iterator begin() const noexcept; iterator end() noexcept; const_iterator end() const noexcept; reverse_iterator rbegin() noexcept; const_reverse_iterator rbegin() const noexcept; reverse_iterator rend() noexcept; const_reverse_iterator rend() const noexcept; const_iterator cbegin() const noexcept; const_iterator cend() const noexcept; const_reverse_iterator crbegin() const noexcept; const_reverse_iterator crend() const noexcept; // capacity: size_type size() const noexcept; bool empty() const noexcept; // element access: reference front(); const_reference front() const; reference back(); const_reference back() const; // modifiers: template void emplace_front(Args&&... args); template void emplace_back(Args&&... args); void push_front(std::unique_ptr node); void push_front(T&& node); void push_back(std::unique_ptr node); void push_back(T&& node); void pop_front(); void pop_back(); std::unique_ptr extract_front(); std::unique_ptr extract_back(); template iterator emplace(iterator pos, Args&&... args); iterator insert(iterator pos, std::unique_ptr node); iterator insert(iterator pos, T&& node); std::unique_ptr extract(iterator it); iterator erase(iterator pos); iterator erase(iterator first, iterator last); void swap(intrusive_list&); void clear() noexcept; void splice(iterator pos, intrusive_list& node); void splice(iterator pos, intrusive_list&& node); void splice(iterator pos, intrusive_list& node, iterator it); void splice(iterator pos, intrusive_list& node, iterator first, iterator last); private: T* first_ = nullptr; T* last_ = nullptr; size_t size_ = 0; }; /// iterator template class intrusive_list::iterator { public: using difference_type = std::ptrdiff_t; using iterator_category = std::bidirectional_iterator_tag; using value_type = T; using pointer = T*; using reference = T&; iterator(const intrusive_list& list, T* node) : list_(&list), node_(node) {} reference operator*() const { assert(node_); return *node_; } pointer operator->() const { assert(node_); return node_; } iterator& operator++() { assert(node_); node_ = node_->next_; return *this; } iterator operator++(int) { iterator tmp = *this; operator++(); return tmp; } iterator& operator--() { node_ = node_ ? node_->prev_ : list_->last_; return *this; } iterator operator--(int) { iterator tmp = *this; operator--(); return tmp; } bool operator==(iterator rhs) const { assert(list_ == rhs.list_); return node_ == rhs.node_; } bool operator!=(iterator rhs) const { assert(list_ == rhs.list_); return node_ != rhs.node_; } private: friend class const_iterator; const intrusive_list* list_; T* node_; }; /// const_iterator template class intrusive_list::const_iterator { public: using difference_type = std::ptrdiff_t; using iterator_category = std::bidirectional_iterator_tag; using value_type = T; using pointer = const T*; using reference = const T&; const_iterator(const intrusive_list& list, T* node) : list_(&list), node_(node) {} const_iterator(const iterator& other) : list_(other.list_), node_(other.node_) {} reference operator*() const { assert(node_); return *node_; } pointer operator->() const { assert(node_); return node_; } const_iterator& operator++() { assert(node_); node_ = node_->next_; return *this; } const_iterator operator++(int) { const_iterator tmp = *this; operator++(); return tmp; } const_iterator& operator--() { node_ = node_ ? node_->prev_ : list_->last_; return *this; } const_iterator operator--(int) { const_iterator tmp = *this; operator--(); return tmp; } bool operator==(const_iterator rhs) const { assert(list_ == rhs.list_); return node_ == rhs.node_; } bool operator!=(const_iterator rhs) const { assert(list_ == rhs.list_); return node_ != rhs.node_; } private: const intrusive_list* list_; T* node_; }; template inline intrusive_list::intrusive_list() {} template inline intrusive_list::intrusive_list(std::unique_ptr node) { push_back(std::move(node)); } template inline intrusive_list::intrusive_list(T&& node) { push_back(std::move(node)); } template inline intrusive_list::intrusive_list(intrusive_list&& other) : first_(other.first_), last_(other.last_), size_(other.size_) { other.first_ = other.last_ = nullptr; other.size_ = 0; } template inline intrusive_list::~intrusive_list() { clear(); } template inline intrusive_list& intrusive_list::operator=( intrusive_list&& other) { clear(); first_ = other.first_; last_ = other.last_; size_ = other.size_; other.first_ = other.last_ = nullptr; other.size_ = 0; return *this; } template inline typename intrusive_list::iterator intrusive_list::begin() noexcept { return iterator(*this, first_); } template inline typename intrusive_list::const_iterator intrusive_list::begin() const noexcept { return const_iterator(*this, first_); } template inline typename intrusive_list::iterator intrusive_list::end() noexcept { return iterator(*this, nullptr); } template inline typename intrusive_list::const_iterator intrusive_list::end() const noexcept { return const_iterator(*this, nullptr); } template inline typename intrusive_list::reverse_iterator intrusive_list::rbegin() noexcept { return reverse_iterator(iterator(*this, nullptr)); } template inline typename intrusive_list::const_reverse_iterator intrusive_list::rbegin() const noexcept { return const_reverse_iterator(const_iterator(*this, nullptr)); } template inline typename intrusive_list::reverse_iterator intrusive_list::rend() noexcept { return reverse_iterator(iterator(*this, first_)); } template inline typename intrusive_list::const_reverse_iterator intrusive_list::rend() const noexcept { return const_reverse_iterator(const_iterator(*this, first_)); } template inline typename intrusive_list::const_iterator intrusive_list::cbegin() const noexcept { return const_iterator(*this, first_); } template inline typename intrusive_list::const_iterator intrusive_list::cend() const noexcept { return const_iterator(*this, nullptr); } template inline typename intrusive_list::const_reverse_iterator intrusive_list::crbegin() const noexcept { return const_reverse_iterator(const_iterator(*this, nullptr)); } template inline typename intrusive_list::const_reverse_iterator intrusive_list::crend() const noexcept { return const_reverse_iterator(const_iterator(*this, first_)); } template inline typename intrusive_list::size_type intrusive_list::size() const noexcept { return size_; } template inline bool intrusive_list::empty() const noexcept { return size_ == 0; } template inline typename intrusive_list::reference intrusive_list::front() { assert(!empty()); return *first_; } template inline typename intrusive_list::const_reference intrusive_list::front() const { assert(!empty()); return *first_; } template inline typename intrusive_list::reference intrusive_list::back() { assert(!empty()); return *last_; } template inline typename intrusive_list::const_reference intrusive_list::back() const { assert(!empty()); return *last_; } template template inline void intrusive_list::emplace_front(Args&&... args) { push_front(std::make_unique(std::forward(args)...)); } template template inline void intrusive_list::emplace_back(Args&&... args) { push_back(std::make_unique(std::forward(args)...)); } template inline void intrusive_list::push_front(std::unique_ptr node) { assert(node->prev_ == nullptr && node->next_ == nullptr); T* node_p = node.release(); if (first_) { node_p->next_ = first_; first_->prev_ = node_p; } else { last_ = node_p; } first_ = node_p; size_++; } template inline void intrusive_list::push_front(T&& node) { push_front(std::make_unique(std::move(node))); } template inline void intrusive_list::push_back(std::unique_ptr node) { assert(node->prev_ == nullptr && node->next_ == nullptr); T* node_p = node.release(); if (last_) { node_p->prev_ = last_; last_->next_ = node_p; } else { first_ = node_p; } last_ = node_p; size_++; } template inline void intrusive_list::push_back(T&& node) { push_back(std::make_unique(std::move(node))); } template inline void intrusive_list::pop_front() { extract_front(); } template inline void intrusive_list::pop_back() { extract_back(); } template inline std::unique_ptr intrusive_list::extract_front() { assert(!empty()); T* node = first_; if (first_ == last_) { first_ = last_ = nullptr; } else { first_ = first_->next_; first_->prev_ = nullptr; } node->next_ = node->prev_ = nullptr; size_--; return std::unique_ptr(node); } template inline std::unique_ptr intrusive_list::extract_back() { assert(!empty()); T* node = last_; if (first_ == last_) { first_ = last_ = nullptr; } else { last_ = last_->prev_; last_->next_ = nullptr; } node->next_ = node->prev_ = nullptr; size_--; return std::unique_ptr(node); } template template inline typename intrusive_list::iterator intrusive_list::emplace( iterator pos, Args&&... args) { return insert(pos, std::make_unique(std::forward(args)...)); } template inline typename intrusive_list::iterator intrusive_list::insert( iterator pos, std::unique_ptr node) { assert(node->prev_ == nullptr && node->next_ == nullptr); T* node_p; if (pos == end()) { push_back(std::move(node)); node_p = &back(); } else { node_p = node.release(); node_p->prev_ = pos->prev_; node_p->next_ = &*pos; if (pos->prev_) { pos->prev_->next_ = node_p; } else { first_ = node_p; } pos->prev_ = node_p; size_++; } return iterator(*this, node_p); } template inline typename intrusive_list::iterator intrusive_list::insert( iterator pos, T&& node) { return insert(pos, std::make_unique(std::move(node))); } template inline std::unique_ptr intrusive_list::extract(iterator pos) { assert(!empty()); assert(pos != end()); T* node = &*pos; if (first_ == last_) { first_ = last_ = nullptr; } else { if (node->prev_) { node->prev_->next_ = node->next_; } else { first_ = node->next_; } if (node->next_) { node->next_->prev_ = node->prev_; } else { last_ = node->prev_; } } node->next_ = node->prev_ = nullptr; size_--; return std::unique_ptr(node); } template inline typename intrusive_list::iterator intrusive_list::erase( iterator pos) { iterator next = std::next(pos); extract(pos); return next; } template inline typename intrusive_list::iterator intrusive_list::erase( iterator first, iterator last) { while (first != last) first = erase(first); return first; } template inline void intrusive_list::swap(intrusive_list& other) { std::swap(first_, other.first_); std::swap(last_, other.last_); std::swap(size_, other.size_); } template inline void intrusive_list::clear() noexcept { for (T* iter = first_; iter;) { T* next = iter->next_; delete iter; iter = next; } first_ = last_ = nullptr; size_ = 0; } template inline void intrusive_list::splice(iterator pos, intrusive_list& other) { splice(pos, other, other.begin(), other.end()); } template inline void intrusive_list::splice(iterator pos, intrusive_list&& other) { splice(pos, other, other.begin(), other.end()); } template inline void intrusive_list::splice(iterator pos, intrusive_list& other, iterator it) { insert(pos, other.extract(it)); } template inline void intrusive_list::splice(iterator pos, intrusive_list& other, iterator first, iterator last) { while (first != last) insert(pos, other.extract(first++)); } } // namespace wabt #endif // WABT_INTRUSIVE_LIST_H_