11
11
12
12
#include " internal_defs.h"
13
13
14
+ // TODO: Move the helpers to a header.
15
+ namespace {
16
+ template <typename T> struct isPointer {
17
+ static constexpr bool value = false ;
18
+ };
19
+
20
+ template <typename T> struct isPointer <T *> {
21
+ static constexpr bool value = true ;
22
+ };
23
+ } // namespace
24
+
14
25
namespace scudo {
15
26
16
27
// Intrusive POD singly and doubly linked list.
17
28
// An object with all zero fields should represent a valid empty list. clear()
18
29
// should be called on all non-zero-initialized objects before using.
30
+ //
31
+ // The intrusive list requires the member `Next` (and `Prev` if doubly linked
32
+ // list)` defined in the node type. The type of `Next`/`Prev` can be a pointer
33
+ // or an index to an array. For example, if the storage of the nodes is an
34
+ // array, instead of using a pointer type, linking with an index type can save
35
+ // some space.
36
+ //
37
+ // There are two things to be noticed while using an index type,
38
+ // 1. Call init() to set up the base address of the array.
39
+ // 2. Define `EndOfListVal` as the nil of the list.
40
+
41
+ template <class T , bool LinkWithPtr = isPointer<decltype (T::Next)>::value>
42
+ class LinkOp {
43
+ public:
44
+ LinkOp () = default ;
45
+ LinkOp (UNUSED T *BaseT, UNUSED uptr BaseSize) {}
46
+ void init (UNUSED T *LinkBase, UNUSED uptr Size) {}
47
+ T *getBase () const { return nullptr ; }
48
+ uptr getSize () const { return 0 ; }
49
+
50
+ T *getNext (T *X) const { return X->Next ; }
51
+ void setNext (T *X, T *Next) const { X->Next = Next; }
52
+
53
+ T *getPrev (T *X) const { return X->Prev ; }
54
+ void setPrev (T *X, T *Prev) const { X->Prev = Prev; }
55
+
56
+ T *getEndOfListVal () const { return nullptr ; }
57
+ };
58
+
59
+ template <class T > class LinkOp <T, /* LinkWithPtr=*/ false > {
60
+ public:
61
+ using LinkTy = decltype (T::Next);
62
+
63
+ LinkOp () = default ;
64
+ LinkOp (T *BaseT, uptr BaseSize) : Base(BaseT), Size(BaseSize) {}
65
+ void init (T *LinkBase, uptr BaseSize) {
66
+ Base = LinkBase;
67
+ // TODO: Check if the `BaseSize` can fit in `Size`.
68
+ Size = static_cast <LinkTy>(BaseSize);
69
+ }
70
+ T *getBase () const { return Base; }
71
+ LinkTy getSize () const { return Size; }
72
+
73
+ T *getNext (T *X) const {
74
+ DCHECK_NE (getBase (), nullptr );
75
+ if (X->Next == getEndOfListVal ())
76
+ return nullptr ;
77
+ DCHECK_LT (X->Next , Size);
78
+ return &Base[X->Next ];
79
+ }
80
+ // Set `X->Next` to `Next`.
81
+ void setNext (T *X, T *Next) const {
82
+ // TODO: Check if the offset fits in the size of `LinkTy`.
83
+ if (Next == nullptr )
84
+ X->Next = getEndOfListVal ();
85
+ else
86
+ X->Next = static_cast <LinkTy>(Next - Base);
87
+ }
19
88
20
- template <class T > class IteratorBase {
89
+ T *getPrev (T *X) const {
90
+ DCHECK_NE (getBase (), nullptr );
91
+ if (X->Prev == getEndOfListVal ())
92
+ return nullptr ;
93
+ DCHECK_LT (X->Prev , Size);
94
+ return &Base[X->Prev ];
95
+ }
96
+ // Set `X->Prev` to `Prev`.
97
+ void setPrev (T *X, T *Prev) const {
98
+ DCHECK_LT (reinterpret_cast <uptr>(Prev),
99
+ reinterpret_cast <uptr>(Base + Size));
100
+ if (Prev == nullptr )
101
+ X->Prev = getEndOfListVal ();
102
+ else
103
+ X->Prev = static_cast <LinkTy>(Prev - Base);
104
+ }
105
+
106
+ // TODO: `LinkTy` should be the same as decltype(T::EndOfListVal).
107
+ LinkTy getEndOfListVal () const { return T::EndOfListVal; }
108
+
109
+ protected:
110
+ T *Base = nullptr ;
111
+ LinkTy Size = 0 ;
112
+ };
113
+
114
+ template <class T > class IteratorBase : public LinkOp <T> {
21
115
public:
22
- explicit IteratorBase (T *CurrentT) : Current(CurrentT) {}
116
+ IteratorBase (const LinkOp<T> &Link, T *CurrentT)
117
+ : LinkOp<T>(Link), Current(CurrentT) {}
118
+
23
119
IteratorBase &operator ++() {
24
- Current = Current-> Next ;
120
+ Current = this -> getNext (Current) ;
25
121
return *this ;
26
122
}
27
123
bool operator !=(IteratorBase Other) const { return Current != Other.Current ; }
@@ -31,7 +127,10 @@ template <class T> class IteratorBase {
31
127
T *Current;
32
128
};
33
129
34
- template <class T > struct IntrusiveList {
130
+ template <class T > struct IntrusiveList : public LinkOp <T> {
131
+ IntrusiveList () = default ;
132
+ void init (T *Base, uptr BaseSize) { LinkOp<T>::init (Base, BaseSize); }
133
+
35
134
bool empty () const { return Size == 0 ; }
36
135
uptr size () const { return Size; }
37
136
@@ -48,11 +147,21 @@ template <class T> struct IntrusiveList {
48
147
typedef IteratorBase<T> Iterator;
49
148
typedef IteratorBase<const T> ConstIterator;
50
149
51
- Iterator begin () { return Iterator (First); }
52
- Iterator end () { return Iterator (nullptr ); }
150
+ Iterator begin () {
151
+ return Iterator (LinkOp<T>(this ->getBase (), this ->getSize ()), First);
152
+ }
153
+ Iterator end () {
154
+ return Iterator (LinkOp<T>(this ->getBase (), this ->getSize ()), nullptr );
155
+ }
53
156
54
- ConstIterator begin () const { return ConstIterator (First); }
55
- ConstIterator end () const { return ConstIterator (nullptr ); }
157
+ ConstIterator begin () const {
158
+ return ConstIterator (LinkOp<const T>(this ->getBase (), this ->getSize ()),
159
+ First);
160
+ }
161
+ ConstIterator end () const {
162
+ return ConstIterator (LinkOp<const T>(this ->getBase (), this ->getSize ()),
163
+ nullptr );
164
+ }
56
165
57
166
void checkConsistency () const ;
58
167
@@ -68,13 +177,13 @@ template <class T> void IntrusiveList<T>::checkConsistency() const {
68
177
CHECK_EQ (Last, nullptr );
69
178
} else {
70
179
uptr Count = 0 ;
71
- for (T *I = First;; I = I-> Next ) {
180
+ for (T *I = First;; I = this -> getNext (I) ) {
72
181
Count++;
73
182
if (I == Last)
74
183
break ;
75
184
}
76
185
CHECK_EQ (this ->size (), Count);
77
- CHECK_EQ (Last-> Next , nullptr );
186
+ CHECK_EQ (this -> getNext (Last) , nullptr );
78
187
}
79
188
}
80
189
@@ -83,28 +192,31 @@ template <class T> struct SinglyLinkedList : public IntrusiveList<T> {
83
192
using IntrusiveList<T>::Last;
84
193
using IntrusiveList<T>::Size;
85
194
using IntrusiveList<T>::empty;
195
+ using IntrusiveList<T>::setNext;
196
+ using IntrusiveList<T>::getNext;
197
+ using IntrusiveList<T>::getEndOfListVal;
86
198
87
199
void push_back (T *X) {
88
- X-> Next = nullptr ;
200
+ setNext (X, nullptr ) ;
89
201
if (empty ())
90
202
First = X;
91
203
else
92
- Last-> Next = X ;
204
+ setNext ( Last, X) ;
93
205
Last = X;
94
206
Size++;
95
207
}
96
208
97
209
void push_front (T *X) {
98
210
if (empty ())
99
211
Last = X;
100
- X-> Next = First;
212
+ setNext (X, First) ;
101
213
First = X;
102
214
Size++;
103
215
}
104
216
105
217
void pop_front () {
106
218
DCHECK (!empty ());
107
- First = First-> Next ;
219
+ First = getNext ( First) ;
108
220
if (!First)
109
221
Last = nullptr ;
110
222
Size--;
@@ -115,8 +227,8 @@ template <class T> struct SinglyLinkedList : public IntrusiveList<T> {
115
227
DCHECK (!empty ());
116
228
DCHECK_NE (Prev, nullptr );
117
229
DCHECK_NE (X, nullptr );
118
- X-> Next = Prev-> Next ;
119
- Prev-> Next = X ;
230
+ setNext (X, getNext ( Prev)) ;
231
+ setNext ( Prev, X) ;
120
232
if (Last == Prev)
121
233
Last = X;
122
234
++Size;
@@ -126,8 +238,8 @@ template <class T> struct SinglyLinkedList : public IntrusiveList<T> {
126
238
DCHECK (!empty ());
127
239
DCHECK_NE (Prev, nullptr );
128
240
DCHECK_NE (X, nullptr );
129
- DCHECK_EQ (Prev-> Next , X);
130
- Prev-> Next = X-> Next ;
241
+ DCHECK_EQ (getNext ( Prev) , X);
242
+ setNext ( Prev, getNext (X)) ;
131
243
if (Last == X)
132
244
Last = Prev;
133
245
Size--;
@@ -140,7 +252,7 @@ template <class T> struct SinglyLinkedList : public IntrusiveList<T> {
140
252
if (empty ()) {
141
253
*this = *L;
142
254
} else {
143
- Last-> Next = L->First ;
255
+ setNext ( Last, L->First ) ;
144
256
Last = L->Last ;
145
257
Size += L->size ();
146
258
}
@@ -153,16 +265,21 @@ template <class T> struct DoublyLinkedList : IntrusiveList<T> {
153
265
using IntrusiveList<T>::Last;
154
266
using IntrusiveList<T>::Size;
155
267
using IntrusiveList<T>::empty;
268
+ using IntrusiveList<T>::setNext;
269
+ using IntrusiveList<T>::getNext;
270
+ using IntrusiveList<T>::setPrev;
271
+ using IntrusiveList<T>::getPrev;
272
+ using IntrusiveList<T>::getEndOfListVal;
156
273
157
274
void push_front (T *X) {
158
- X-> Prev = nullptr ;
275
+ setPrev (X, nullptr ) ;
159
276
if (empty ()) {
160
277
Last = X;
161
278
} else {
162
- DCHECK_EQ (First-> Prev , nullptr );
163
- First-> Prev = X ;
279
+ DCHECK_EQ (getPrev ( First) , nullptr );
280
+ setPrev ( First, X) ;
164
281
}
165
- X-> Next = First;
282
+ setNext (X, First) ;
166
283
First = X;
167
284
Size++;
168
285
}
@@ -171,53 +288,53 @@ template <class T> struct DoublyLinkedList : IntrusiveList<T> {
171
288
void insert (T *X, T *Y) {
172
289
if (Y == First)
173
290
return push_front (X);
174
- T *Prev = Y-> Prev ;
291
+ T *Prev = getPrev (Y) ;
175
292
// This is a hard CHECK to ensure consistency in the event of an intentional
176
293
// 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 ;
294
+ CHECK_EQ (getNext ( Prev) , Y);
295
+ setNext ( Prev, X) ;
296
+ setPrev (X, Prev) ;
297
+ setNext (X, Y) ;
298
+ setPrev (Y, X) ;
182
299
Size++;
183
300
}
184
301
185
302
void push_back (T *X) {
186
- X-> Next = nullptr ;
303
+ setNext (X, nullptr ) ;
187
304
if (empty ()) {
188
305
First = X;
189
306
} else {
190
- DCHECK_EQ (Last-> Next , nullptr );
191
- Last-> Next = X ;
307
+ DCHECK_EQ (getNext ( Last) , nullptr );
308
+ setNext ( Last, X) ;
192
309
}
193
- X-> Prev = Last;
310
+ setPrev (X, Last) ;
194
311
Last = X;
195
312
Size++;
196
313
}
197
314
198
315
void pop_front () {
199
316
DCHECK (!empty ());
200
- First = First-> Next ;
317
+ First = getNext ( First) ;
201
318
if (!First)
202
319
Last = nullptr ;
203
320
else
204
- First-> Prev = nullptr ;
321
+ setPrev ( First, nullptr ) ;
205
322
Size--;
206
323
}
207
324
208
325
// The consistency of the adjacent links is aggressively checked in order to
209
326
// catch potential corruption attempts, that could yield a mirrored
210
327
// write-{4,8} primitive. nullptr checks are deemed less vital.
211
328
void remove (T *X) {
212
- T *Prev = X-> Prev ;
213
- T *Next = X-> Next ;
329
+ T *Prev = getPrev (X) ;
330
+ T *Next = getNext (X) ;
214
331
if (Prev) {
215
- CHECK_EQ (Prev-> Next , X);
216
- Prev-> Next = Next;
332
+ CHECK_EQ (getNext ( Prev) , X);
333
+ setNext ( Prev, Next) ;
217
334
}
218
335
if (Next) {
219
- CHECK_EQ (Next-> Prev , X);
220
- Next-> Prev = Prev;
336
+ CHECK_EQ (getPrev ( Next) , X);
337
+ setPrev ( Next, Prev) ;
221
338
}
222
339
if (First == X) {
223
340
DCHECK_EQ (Prev, nullptr );
0 commit comments