| //===- llvm/ADT/AllocatorList.h - Custom allocator list ---------*- C++ -*-===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #ifndef LLVM_ADT_ALLOCATORLIST_H |
| #define LLVM_ADT_ALLOCATORLIST_H |
| |
| #include "llvm/ADT/ilist_node.h" |
| #include "llvm/ADT/iterator.h" |
| #include "llvm/ADT/simple_ilist.h" |
| #include "llvm/Support/Allocator.h" |
| #include <cassert> |
| #include <cstddef> |
| #include <iterator> |
| #include <type_traits> |
| #include <utility> |
| |
| namespace llvm { |
| |
| /// A linked-list with a custom, local allocator. |
| /// |
| /// Expose a std::list-like interface that owns and uses a custom LLVM-style |
| /// allocator (e.g., BumpPtrAllocator), leveraging \a simple_ilist for the |
| /// implementation details. |
| /// |
| /// Because this list owns the allocator, calling \a splice() with a different |
| /// list isn't generally safe. As such, \a splice has been left out of the |
| /// interface entirely. |
| template <class T, class AllocatorT> class AllocatorList : AllocatorT { |
| struct Node : ilist_node<Node> { |
| Node(Node &&) = delete; |
| Node(const Node &) = delete; |
| Node &operator=(Node &&) = delete; |
| Node &operator=(const Node &) = delete; |
| |
| Node(T &&V) : V(std::move(V)) {} |
| Node(const T &V) : V(V) {} |
| template <class... Ts> Node(Ts &&... Vs) : V(std::forward<Ts>(Vs)...) {} |
| T V; |
| }; |
| |
| using list_type = simple_ilist<Node>; |
| |
| list_type List; |
| |
| AllocatorT &getAlloc() { return *this; } |
| const AllocatorT &getAlloc() const { return *this; } |
| |
| template <class... ArgTs> Node *create(ArgTs &&... Args) { |
| return new (getAlloc()) Node(std::forward<ArgTs>(Args)...); |
| } |
| |
| struct Cloner { |
| AllocatorList &AL; |
| |
| Cloner(AllocatorList &AL) : AL(AL) {} |
| |
| Node *operator()(const Node &N) const { return AL.create(N.V); } |
| }; |
| |
| struct Disposer { |
| AllocatorList &AL; |
| |
| Disposer(AllocatorList &AL) : AL(AL) {} |
| |
| void operator()(Node *N) const { |
| N->~Node(); |
| AL.getAlloc().Deallocate(N); |
| } |
| }; |
| |
| public: |
| using value_type = T; |
| using pointer = T *; |
| using reference = T &; |
| using const_pointer = const T *; |
| using const_reference = const T &; |
| using size_type = typename list_type::size_type; |
| using difference_type = typename list_type::difference_type; |
| |
| private: |
| template <class ValueT, class IteratorBase> |
| class IteratorImpl |
| : public iterator_adaptor_base<IteratorImpl<ValueT, IteratorBase>, |
| IteratorBase, |
| std::bidirectional_iterator_tag, ValueT> { |
| template <class OtherValueT, class OtherIteratorBase> |
| friend class IteratorImpl; |
| friend AllocatorList; |
| |
| using base_type = |
| iterator_adaptor_base<IteratorImpl<ValueT, IteratorBase>, IteratorBase, |
| std::bidirectional_iterator_tag, ValueT>; |
| |
| public: |
| using value_type = ValueT; |
| using pointer = ValueT *; |
| using reference = ValueT &; |
| |
| IteratorImpl() = default; |
| IteratorImpl(const IteratorImpl &) = default; |
| IteratorImpl &operator=(const IteratorImpl &) = default; |
| |
| explicit IteratorImpl(const IteratorBase &I) : base_type(I) {} |
| |
| template <class OtherValueT, class OtherIteratorBase> |
| IteratorImpl(const IteratorImpl<OtherValueT, OtherIteratorBase> &X, |
| std::enable_if_t<std::is_convertible< |
| OtherIteratorBase, IteratorBase>::value> * = nullptr) |
| : base_type(X.wrapped()) {} |
| |
| ~IteratorImpl() = default; |
| |
| reference operator*() const { return base_type::wrapped()->V; } |
| pointer operator->() const { return &operator*(); } |
| }; |
| |
| public: |
| using iterator = IteratorImpl<T, typename list_type::iterator>; |
| using reverse_iterator = |
| IteratorImpl<T, typename list_type::reverse_iterator>; |
| using const_iterator = |
| IteratorImpl<const T, typename list_type::const_iterator>; |
| using const_reverse_iterator = |
| IteratorImpl<const T, typename list_type::const_reverse_iterator>; |
| |
| AllocatorList() = default; |
| AllocatorList(AllocatorList &&X) |
| : AllocatorT(std::move(X.getAlloc())), List(std::move(X.List)) {} |
| |
| AllocatorList(const AllocatorList &X) { |
| List.cloneFrom(X.List, Cloner(*this), Disposer(*this)); |
| } |
| |
| AllocatorList &operator=(AllocatorList &&X) { |
| clear(); // Dispose of current nodes explicitly. |
| List = std::move(X.List); |
| getAlloc() = std::move(X.getAlloc()); |
| return *this; |
| } |
| |
| AllocatorList &operator=(const AllocatorList &X) { |
| List.cloneFrom(X.List, Cloner(*this), Disposer(*this)); |
| return *this; |
| } |
| |
| ~AllocatorList() { clear(); } |
| |
| void swap(AllocatorList &RHS) { |
| List.swap(RHS.List); |
| std::swap(getAlloc(), RHS.getAlloc()); |
| } |
| |
| bool empty() { return List.empty(); } |
| size_t size() { return List.size(); } |
| |
| iterator begin() { return iterator(List.begin()); } |
| iterator end() { return iterator(List.end()); } |
| const_iterator begin() const { return const_iterator(List.begin()); } |
| const_iterator end() const { return const_iterator(List.end()); } |
| reverse_iterator rbegin() { return reverse_iterator(List.rbegin()); } |
| reverse_iterator rend() { return reverse_iterator(List.rend()); } |
| const_reverse_iterator rbegin() const { |
| return const_reverse_iterator(List.rbegin()); |
| } |
| const_reverse_iterator rend() const { |
| return const_reverse_iterator(List.rend()); |
| } |
| |
| T &back() { return List.back().V; } |
| T &front() { return List.front().V; } |
| const T &back() const { return List.back().V; } |
| const T &front() const { return List.front().V; } |
| |
| template <class... Ts> iterator emplace(iterator I, Ts &&... Vs) { |
| return iterator(List.insert(I.wrapped(), *create(std::forward<Ts>(Vs)...))); |
| } |
| |
| iterator insert(iterator I, T &&V) { |
| return iterator(List.insert(I.wrapped(), *create(std::move(V)))); |
| } |
| iterator insert(iterator I, const T &V) { |
| return iterator(List.insert(I.wrapped(), *create(V))); |
| } |
| |
| template <class Iterator> |
| void insert(iterator I, Iterator First, Iterator Last) { |
| for (; First != Last; ++First) |
| List.insert(I.wrapped(), *create(*First)); |
| } |
| |
| iterator erase(iterator I) { |
| return iterator(List.eraseAndDispose(I.wrapped(), Disposer(*this))); |
| } |
| |
| iterator erase(iterator First, iterator Last) { |
| return iterator( |
| List.eraseAndDispose(First.wrapped(), Last.wrapped(), Disposer(*this))); |
| } |
| |
| void clear() { List.clearAndDispose(Disposer(*this)); } |
| void pop_back() { List.eraseAndDispose(--List.end(), Disposer(*this)); } |
| void pop_front() { List.eraseAndDispose(List.begin(), Disposer(*this)); } |
| void push_back(T &&V) { insert(end(), std::move(V)); } |
| void push_front(T &&V) { insert(begin(), std::move(V)); } |
| void push_back(const T &V) { insert(end(), V); } |
| void push_front(const T &V) { insert(begin(), V); } |
| template <class... Ts> void emplace_back(Ts &&... Vs) { |
| emplace(end(), std::forward<Ts>(Vs)...); |
| } |
| template <class... Ts> void emplace_front(Ts &&... Vs) { |
| emplace(begin(), std::forward<Ts>(Vs)...); |
| } |
| |
| /// Reset the underlying allocator. |
| /// |
| /// \pre \c empty() |
| void resetAlloc() { |
| assert(empty() && "Cannot reset allocator if not empty"); |
| getAlloc().Reset(); |
| } |
| }; |
| |
| template <class T> using BumpPtrList = AllocatorList<T, BumpPtrAllocator>; |
| |
| } // end namespace llvm |
| |
| #endif // LLVM_ADT_ALLOCATORLIST_H |