Details | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 14 | pmbaty | 1 | //===- llvm/ADT/AllocatorList.h - Custom allocator list ---------*- C++ -*-===// |
| 2 | // |
||
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
||
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
||
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
||
| 6 | // |
||
| 7 | //===----------------------------------------------------------------------===// |
||
| 8 | |||
| 9 | #ifndef LLVM_ADT_ALLOCATORLIST_H |
||
| 10 | #define LLVM_ADT_ALLOCATORLIST_H |
||
| 11 | |||
| 12 | #include "llvm/ADT/ilist_node.h" |
||
| 13 | #include "llvm/ADT/iterator.h" |
||
| 14 | #include "llvm/ADT/simple_ilist.h" |
||
| 15 | #include "llvm/Support/Allocator.h" |
||
| 16 | #include <cassert> |
||
| 17 | #include <cstddef> |
||
| 18 | #include <iterator> |
||
| 19 | #include <type_traits> |
||
| 20 | #include <utility> |
||
| 21 | |||
| 22 | namespace llvm { |
||
| 23 | |||
| 24 | /// A linked-list with a custom, local allocator. |
||
| 25 | /// |
||
| 26 | /// Expose a std::list-like interface that owns and uses a custom LLVM-style |
||
| 27 | /// allocator (e.g., BumpPtrAllocator), leveraging \a simple_ilist for the |
||
| 28 | /// implementation details. |
||
| 29 | /// |
||
| 30 | /// Because this list owns the allocator, calling \a splice() with a different |
||
| 31 | /// list isn't generally safe. As such, \a splice has been left out of the |
||
| 32 | /// interface entirely. |
||
| 33 | template <class T, class AllocatorT> class AllocatorList : AllocatorT { |
||
| 34 | struct Node : ilist_node<Node> { |
||
| 35 | Node(Node &&) = delete; |
||
| 36 | Node(const Node &) = delete; |
||
| 37 | Node &operator=(Node &&) = delete; |
||
| 38 | Node &operator=(const Node &) = delete; |
||
| 39 | |||
| 40 | Node(T &&V) : V(std::move(V)) {} |
||
| 41 | Node(const T &V) : V(V) {} |
||
| 42 | template <class... Ts> Node(Ts &&... Vs) : V(std::forward<Ts>(Vs)...) {} |
||
| 43 | T V; |
||
| 44 | }; |
||
| 45 | |||
| 46 | using list_type = simple_ilist<Node>; |
||
| 47 | |||
| 48 | list_type List; |
||
| 49 | |||
| 50 | AllocatorT &getAlloc() { return *this; } |
||
| 51 | const AllocatorT &getAlloc() const { return *this; } |
||
| 52 | |||
| 53 | template <class... ArgTs> Node *create(ArgTs &&... Args) { |
||
| 54 | return new (getAlloc()) Node(std::forward<ArgTs>(Args)...); |
||
| 55 | } |
||
| 56 | |||
| 57 | struct Cloner { |
||
| 58 | AllocatorList &AL; |
||
| 59 | |||
| 60 | Cloner(AllocatorList &AL) : AL(AL) {} |
||
| 61 | |||
| 62 | Node *operator()(const Node &N) const { return AL.create(N.V); } |
||
| 63 | }; |
||
| 64 | |||
| 65 | struct Disposer { |
||
| 66 | AllocatorList &AL; |
||
| 67 | |||
| 68 | Disposer(AllocatorList &AL) : AL(AL) {} |
||
| 69 | |||
| 70 | void operator()(Node *N) const { |
||
| 71 | N->~Node(); |
||
| 72 | AL.getAlloc().Deallocate(N); |
||
| 73 | } |
||
| 74 | }; |
||
| 75 | |||
| 76 | public: |
||
| 77 | using value_type = T; |
||
| 78 | using pointer = T *; |
||
| 79 | using reference = T &; |
||
| 80 | using const_pointer = const T *; |
||
| 81 | using const_reference = const T &; |
||
| 82 | using size_type = typename list_type::size_type; |
||
| 83 | using difference_type = typename list_type::difference_type; |
||
| 84 | |||
| 85 | private: |
||
| 86 | template <class ValueT, class IteratorBase> |
||
| 87 | class IteratorImpl |
||
| 88 | : public iterator_adaptor_base<IteratorImpl<ValueT, IteratorBase>, |
||
| 89 | IteratorBase, |
||
| 90 | std::bidirectional_iterator_tag, ValueT> { |
||
| 91 | template <class OtherValueT, class OtherIteratorBase> |
||
| 92 | friend class IteratorImpl; |
||
| 93 | friend AllocatorList; |
||
| 94 | |||
| 95 | using base_type = |
||
| 96 | iterator_adaptor_base<IteratorImpl<ValueT, IteratorBase>, IteratorBase, |
||
| 97 | std::bidirectional_iterator_tag, ValueT>; |
||
| 98 | |||
| 99 | public: |
||
| 100 | using value_type = ValueT; |
||
| 101 | using pointer = ValueT *; |
||
| 102 | using reference = ValueT &; |
||
| 103 | |||
| 104 | IteratorImpl() = default; |
||
| 105 | IteratorImpl(const IteratorImpl &) = default; |
||
| 106 | IteratorImpl &operator=(const IteratorImpl &) = default; |
||
| 107 | |||
| 108 | explicit IteratorImpl(const IteratorBase &I) : base_type(I) {} |
||
| 109 | |||
| 110 | template <class OtherValueT, class OtherIteratorBase> |
||
| 111 | IteratorImpl(const IteratorImpl<OtherValueT, OtherIteratorBase> &X, |
||
| 112 | std::enable_if_t<std::is_convertible< |
||
| 113 | OtherIteratorBase, IteratorBase>::value> * = nullptr) |
||
| 114 | : base_type(X.wrapped()) {} |
||
| 115 | |||
| 116 | ~IteratorImpl() = default; |
||
| 117 | |||
| 118 | reference operator*() const { return base_type::wrapped()->V; } |
||
| 119 | pointer operator->() const { return &operator*(); } |
||
| 120 | }; |
||
| 121 | |||
| 122 | public: |
||
| 123 | using iterator = IteratorImpl<T, typename list_type::iterator>; |
||
| 124 | using reverse_iterator = |
||
| 125 | IteratorImpl<T, typename list_type::reverse_iterator>; |
||
| 126 | using const_iterator = |
||
| 127 | IteratorImpl<const T, typename list_type::const_iterator>; |
||
| 128 | using const_reverse_iterator = |
||
| 129 | IteratorImpl<const T, typename list_type::const_reverse_iterator>; |
||
| 130 | |||
| 131 | AllocatorList() = default; |
||
| 132 | AllocatorList(AllocatorList &&X) |
||
| 133 | : AllocatorT(std::move(X.getAlloc())), List(std::move(X.List)) {} |
||
| 134 | |||
| 135 | AllocatorList(const AllocatorList &X) { |
||
| 136 | List.cloneFrom(X.List, Cloner(*this), Disposer(*this)); |
||
| 137 | } |
||
| 138 | |||
| 139 | AllocatorList &operator=(AllocatorList &&X) { |
||
| 140 | clear(); // Dispose of current nodes explicitly. |
||
| 141 | List = std::move(X.List); |
||
| 142 | getAlloc() = std::move(X.getAlloc()); |
||
| 143 | return *this; |
||
| 144 | } |
||
| 145 | |||
| 146 | AllocatorList &operator=(const AllocatorList &X) { |
||
| 147 | List.cloneFrom(X.List, Cloner(*this), Disposer(*this)); |
||
| 148 | return *this; |
||
| 149 | } |
||
| 150 | |||
| 151 | ~AllocatorList() { clear(); } |
||
| 152 | |||
| 153 | void swap(AllocatorList &RHS) { |
||
| 154 | List.swap(RHS.List); |
||
| 155 | std::swap(getAlloc(), RHS.getAlloc()); |
||
| 156 | } |
||
| 157 | |||
| 158 | bool empty() { return List.empty(); } |
||
| 159 | size_t size() { return List.size(); } |
||
| 160 | |||
| 161 | iterator begin() { return iterator(List.begin()); } |
||
| 162 | iterator end() { return iterator(List.end()); } |
||
| 163 | const_iterator begin() const { return const_iterator(List.begin()); } |
||
| 164 | const_iterator end() const { return const_iterator(List.end()); } |
||
| 165 | reverse_iterator rbegin() { return reverse_iterator(List.rbegin()); } |
||
| 166 | reverse_iterator rend() { return reverse_iterator(List.rend()); } |
||
| 167 | const_reverse_iterator rbegin() const { |
||
| 168 | return const_reverse_iterator(List.rbegin()); |
||
| 169 | } |
||
| 170 | const_reverse_iterator rend() const { |
||
| 171 | return const_reverse_iterator(List.rend()); |
||
| 172 | } |
||
| 173 | |||
| 174 | T &back() { return List.back().V; } |
||
| 175 | T &front() { return List.front().V; } |
||
| 176 | const T &back() const { return List.back().V; } |
||
| 177 | const T &front() const { return List.front().V; } |
||
| 178 | |||
| 179 | template <class... Ts> iterator emplace(iterator I, Ts &&... Vs) { |
||
| 180 | return iterator(List.insert(I.wrapped(), *create(std::forward<Ts>(Vs)...))); |
||
| 181 | } |
||
| 182 | |||
| 183 | iterator insert(iterator I, T &&V) { |
||
| 184 | return iterator(List.insert(I.wrapped(), *create(std::move(V)))); |
||
| 185 | } |
||
| 186 | iterator insert(iterator I, const T &V) { |
||
| 187 | return iterator(List.insert(I.wrapped(), *create(V))); |
||
| 188 | } |
||
| 189 | |||
| 190 | template <class Iterator> |
||
| 191 | void insert(iterator I, Iterator First, Iterator Last) { |
||
| 192 | for (; First != Last; ++First) |
||
| 193 | List.insert(I.wrapped(), *create(*First)); |
||
| 194 | } |
||
| 195 | |||
| 196 | iterator erase(iterator I) { |
||
| 197 | return iterator(List.eraseAndDispose(I.wrapped(), Disposer(*this))); |
||
| 198 | } |
||
| 199 | |||
| 200 | iterator erase(iterator First, iterator Last) { |
||
| 201 | return iterator( |
||
| 202 | List.eraseAndDispose(First.wrapped(), Last.wrapped(), Disposer(*this))); |
||
| 203 | } |
||
| 204 | |||
| 205 | void clear() { List.clearAndDispose(Disposer(*this)); } |
||
| 206 | void pop_back() { List.eraseAndDispose(--List.end(), Disposer(*this)); } |
||
| 207 | void pop_front() { List.eraseAndDispose(List.begin(), Disposer(*this)); } |
||
| 208 | void push_back(T &&V) { insert(end(), std::move(V)); } |
||
| 209 | void push_front(T &&V) { insert(begin(), std::move(V)); } |
||
| 210 | void push_back(const T &V) { insert(end(), V); } |
||
| 211 | void push_front(const T &V) { insert(begin(), V); } |
||
| 212 | template <class... Ts> void emplace_back(Ts &&... Vs) { |
||
| 213 | emplace(end(), std::forward<Ts>(Vs)...); |
||
| 214 | } |
||
| 215 | template <class... Ts> void emplace_front(Ts &&... Vs) { |
||
| 216 | emplace(begin(), std::forward<Ts>(Vs)...); |
||
| 217 | } |
||
| 218 | |||
| 219 | /// Reset the underlying allocator. |
||
| 220 | /// |
||
| 221 | /// \pre \c empty() |
||
| 222 | void resetAlloc() { |
||
| 223 | assert(empty() && "Cannot reset allocator if not empty"); |
||
| 224 | getAlloc().Reset(); |
||
| 225 | } |
||
| 226 | }; |
||
| 227 | |||
| 228 | template <class T> using BumpPtrList = AllocatorList<T, BumpPtrAllocator>; |
||
| 229 | |||
| 230 | } // end namespace llvm |
||
| 231 | |||
| 232 | #endif // LLVM_ADT_ALLOCATORLIST_H |