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 
NodeNode40     Node(T &&V) : V(std::move(V)) {}
NodeNode41     Node(const T &V) : V(V) {}
NodeNode42     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 
getAlloc()50   AllocatorT &getAlloc() { return *this; }
getAlloc()51   const AllocatorT &getAlloc() const { return *this; }
52 
create(ArgTs &&...Args)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 
ClonerCloner60     Cloner(AllocatorList &AL) : AL(AL) {}
61 
operatorCloner62     Node *operator()(const Node &N) const { return AL.create(N.V); }
63   };
64 
65   struct Disposer {
66     AllocatorList &AL;
67 
DisposerDisposer68     Disposer(AllocatorList &AL) : AL(AL) {}
69 
operatorDisposer70     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 
IteratorImpl(const IteratorBase & I)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;
AllocatorList(AllocatorList && X)132   AllocatorList(AllocatorList &&X)
133       : AllocatorT(std::move(X.getAlloc())), List(std::move(X.List)) {}
134 
AllocatorList(const AllocatorList & X)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 
~AllocatorList()151   ~AllocatorList() { clear(); }
152 
swap(AllocatorList & RHS)153   void swap(AllocatorList &RHS) {
154     List.swap(RHS.List);
155     std::swap(getAlloc(), RHS.getAlloc());
156   }
157 
empty()158   bool empty() { return List.empty(); }
size()159   size_t size() { return List.size(); }
160 
begin()161   iterator begin() { return iterator(List.begin()); }
end()162   iterator end() { return iterator(List.end()); }
begin()163   const_iterator begin() const { return const_iterator(List.begin()); }
end()164   const_iterator end() const { return const_iterator(List.end()); }
rbegin()165   reverse_iterator rbegin() { return reverse_iterator(List.rbegin()); }
rend()166   reverse_iterator rend() { return reverse_iterator(List.rend()); }
rbegin()167   const_reverse_iterator rbegin() const {
168     return const_reverse_iterator(List.rbegin());
169   }
rend()170   const_reverse_iterator rend() const {
171     return const_reverse_iterator(List.rend());
172   }
173 
back()174   T &back() { return List.back().V; }
front()175   T &front() { return List.front().V; }
back()176   const T &back() const { return List.back().V; }
front()177   const T &front() const { return List.front().V; }
178 
emplace(iterator I,Ts &&...Vs)179   template <class... Ts> iterator emplace(iterator I, Ts &&... Vs) {
180     return iterator(List.insert(I.wrapped(), *create(std::forward<Ts>(Vs)...)));
181   }
182 
insert(iterator I,T && V)183   iterator insert(iterator I, T &&V) {
184     return iterator(List.insert(I.wrapped(), *create(std::move(V))));
185   }
insert(iterator I,const T & V)186   iterator insert(iterator I, const T &V) {
187     return iterator(List.insert(I.wrapped(), *create(V)));
188   }
189 
190   template <class Iterator>
insert(iterator I,Iterator First,Iterator Last)191   void insert(iterator I, Iterator First, Iterator Last) {
192     for (; First != Last; ++First)
193       List.insert(I.wrapped(), *create(*First));
194   }
195 
erase(iterator I)196   iterator erase(iterator I) {
197     return iterator(List.eraseAndDispose(I.wrapped(), Disposer(*this)));
198   }
199 
erase(iterator First,iterator Last)200   iterator erase(iterator First, iterator Last) {
201     return iterator(
202         List.eraseAndDispose(First.wrapped(), Last.wrapped(), Disposer(*this)));
203   }
204 
clear()205   void clear() { List.clearAndDispose(Disposer(*this)); }
pop_back()206   void pop_back() { List.eraseAndDispose(--List.end(), Disposer(*this)); }
pop_front()207   void pop_front() { List.eraseAndDispose(List.begin(), Disposer(*this)); }
push_back(T && V)208   void push_back(T &&V) { insert(end(), std::move(V)); }
push_front(T && V)209   void push_front(T &&V) { insert(begin(), std::move(V)); }
push_back(const T & V)210   void push_back(const T &V) { insert(end(), V); }
push_front(const T & V)211   void push_front(const T &V) { insert(begin(), V); }
emplace_back(Ts &&...Vs)212   template <class... Ts> void emplace_back(Ts &&... Vs) {
213     emplace(end(), std::forward<Ts>(Vs)...);
214   }
emplace_front(Ts &&...Vs)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()
resetAlloc()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
233