1 | //===-- list.h --------------------------------------------------*- 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 SCUDO_LIST_H_ |
10 | #define SCUDO_LIST_H_ |
11 | |
12 | #include "internal_defs.h" |
13 | |
14 | namespace scudo { |
15 | |
16 | // Intrusive POD singly and doubly linked list. |
17 | // An object with all zero fields should represent a valid empty list. clear() |
18 | // should be called on all non-zero-initialized objects before using. |
19 | |
20 | template <class T> class IteratorBase { |
21 | public: |
22 | explicit IteratorBase(T *CurrentT) : Current(CurrentT) {} |
23 | IteratorBase &operator++() { |
24 | Current = Current->Next; |
25 | return *this; |
26 | } |
27 | bool operator!=(IteratorBase Other) const { return Current != Other.Current; } |
28 | T &operator*() { return *Current; } |
29 | |
30 | private: |
31 | T *Current; |
32 | }; |
33 | |
34 | template <class T> struct IntrusiveList { |
35 | bool empty() const { return Size == 0; } |
36 | uptr size() const { return Size; } |
37 | |
38 | T *front() { return First; } |
39 | const T *front() const { return First; } |
40 | T *back() { return Last; } |
41 | const T *back() const { return Last; } |
42 | |
43 | void clear() { |
44 | First = Last = nullptr; |
45 | Size = 0; |
46 | } |
47 | |
48 | typedef IteratorBase<T> Iterator; |
49 | typedef IteratorBase<const T> ConstIterator; |
50 | |
51 | Iterator begin() { return Iterator(First); } |
52 | Iterator end() { return Iterator(nullptr); } |
53 | |
54 | ConstIterator begin() const { return ConstIterator(First); } |
55 | ConstIterator end() const { return ConstIterator(nullptr); } |
56 | |
57 | void checkConsistency() const; |
58 | |
59 | protected: |
60 | uptr Size = 0; |
61 | T *First = nullptr; |
62 | T *Last = nullptr; |
63 | }; |
64 | |
65 | template <class T> void IntrusiveList<T>::checkConsistency() const { |
66 | if (Size == 0) { |
67 | CHECK_EQ(First, nullptr); |
68 | CHECK_EQ(Last, nullptr); |
69 | } else { |
70 | uptr Count = 0; |
71 | for (T *I = First;; I = I->Next) { |
72 | Count++; |
73 | if (I == Last) |
74 | break; |
75 | } |
76 | CHECK_EQ(this->size(), Count); |
77 | CHECK_EQ(Last->Next, nullptr); |
78 | } |
79 | } |
80 | |
81 | template <class T> struct SinglyLinkedList : public IntrusiveList<T> { |
82 | using IntrusiveList<T>::First; |
83 | using IntrusiveList<T>::Last; |
84 | using IntrusiveList<T>::Size; |
85 | using IntrusiveList<T>::empty; |
86 | |
87 | void push_back(T *X) { |
88 | X->Next = nullptr; |
89 | if (empty()) |
90 | First = X; |
91 | else |
92 | Last->Next = X; |
93 | Last = X; |
94 | Size++; |
95 | } |
96 | |
97 | void push_front(T *X) { |
98 | if (empty()) |
99 | Last = X; |
100 | X->Next = First; |
101 | First = X; |
102 | Size++; |
103 | } |
104 | |
105 | void pop_front() { |
106 | DCHECK(!empty()); |
107 | First = First->Next; |
108 | if (!First) |
109 | Last = nullptr; |
110 | Size--; |
111 | } |
112 | |
113 | // Insert X next to Prev |
114 | void insert(T *Prev, T *X) { |
115 | DCHECK(!empty()); |
116 | DCHECK_NE(Prev, nullptr); |
117 | DCHECK_NE(X, nullptr); |
118 | X->Next = Prev->Next; |
119 | Prev->Next = X; |
120 | if (Last == Prev) |
121 | Last = X; |
122 | ++Size; |
123 | } |
124 | |
125 | void (T *Prev, T *X) { |
126 | DCHECK(!empty()); |
127 | DCHECK_NE(Prev, nullptr); |
128 | DCHECK_NE(X, nullptr); |
129 | DCHECK_EQ(Prev->Next, X); |
130 | Prev->Next = X->Next; |
131 | if (Last == X) |
132 | Last = Prev; |
133 | Size--; |
134 | } |
135 | |
136 | void append_back(SinglyLinkedList<T> *L) { |
137 | DCHECK_NE(this, L); |
138 | if (L->empty()) |
139 | return; |
140 | if (empty()) { |
141 | *this = *L; |
142 | } else { |
143 | Last->Next = L->First; |
144 | Last = L->Last; |
145 | Size += L->size(); |
146 | } |
147 | L->clear(); |
148 | } |
149 | }; |
150 | |
151 | template <class T> struct DoublyLinkedList : IntrusiveList<T> { |
152 | using IntrusiveList<T>::First; |
153 | using IntrusiveList<T>::Last; |
154 | using IntrusiveList<T>::Size; |
155 | using IntrusiveList<T>::empty; |
156 | |
157 | void push_front(T *X) { |
158 | X->Prev = nullptr; |
159 | if (empty()) { |
160 | Last = X; |
161 | } else { |
162 | DCHECK_EQ(First->Prev, nullptr); |
163 | First->Prev = X; |
164 | } |
165 | X->Next = First; |
166 | First = X; |
167 | Size++; |
168 | } |
169 | |
170 | // Inserts X before Y. |
171 | void insert(T *X, T *Y) { |
172 | if (Y == First) |
173 | return push_front(X); |
174 | T *Prev = Y->Prev; |
175 | // This is a hard CHECK to ensure consistency in the event of an intentional |
176 | // corruption of Y->Prev, to prevent a potential write-{4,8}. |
177 | CHECK_EQ(Prev->Next, Y); |
178 | Prev->Next = X; |
179 | X->Prev = Prev; |
180 | X->Next = Y; |
181 | Y->Prev = X; |
182 | Size++; |
183 | } |
184 | |
185 | void push_back(T *X) { |
186 | X->Next = nullptr; |
187 | if (empty()) { |
188 | First = X; |
189 | } else { |
190 | DCHECK_EQ(Last->Next, nullptr); |
191 | Last->Next = X; |
192 | } |
193 | X->Prev = Last; |
194 | Last = X; |
195 | Size++; |
196 | } |
197 | |
198 | void pop_front() { |
199 | DCHECK(!empty()); |
200 | First = First->Next; |
201 | if (!First) |
202 | Last = nullptr; |
203 | else |
204 | First->Prev = nullptr; |
205 | Size--; |
206 | } |
207 | |
208 | // The consistency of the adjacent links is aggressively checked in order to |
209 | // catch potential corruption attempts, that could yield a mirrored |
210 | // write-{4,8} primitive. nullptr checks are deemed less vital. |
211 | void remove(T *X) { |
212 | T *Prev = X->Prev; |
213 | T *Next = X->Next; |
214 | if (Prev) { |
215 | CHECK_EQ(Prev->Next, X); |
216 | Prev->Next = Next; |
217 | } |
218 | if (Next) { |
219 | CHECK_EQ(Next->Prev, X); |
220 | Next->Prev = Prev; |
221 | } |
222 | if (First == X) { |
223 | DCHECK_EQ(Prev, nullptr); |
224 | First = Next; |
225 | } else { |
226 | DCHECK_NE(Prev, nullptr); |
227 | } |
228 | if (Last == X) { |
229 | DCHECK_EQ(Next, nullptr); |
230 | Last = Prev; |
231 | } else { |
232 | DCHECK_NE(Next, nullptr); |
233 | } |
234 | Size--; |
235 | } |
236 | }; |
237 | |
238 | } // namespace scudo |
239 | |
240 | #endif // SCUDO_LIST_H_ |
241 | |