1 | //===-- xray_segmented_array.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 | // This file is a part of XRay, a dynamic runtime instrumentation system. |
10 | // |
11 | // Defines the implementation of a segmented array, with fixed-size segments |
12 | // backing the segments. |
13 | // |
14 | //===----------------------------------------------------------------------===// |
15 | #ifndef XRAY_SEGMENTED_ARRAY_H |
16 | #define XRAY_SEGMENTED_ARRAY_H |
17 | |
18 | #include "sanitizer_common/sanitizer_allocator.h" |
19 | #include "xray_allocator.h" |
20 | #include "xray_utils.h" |
21 | #include <cassert> |
22 | #include <type_traits> |
23 | #include <utility> |
24 | |
25 | namespace __xray { |
26 | |
27 | /// The Array type provides an interface similar to std::vector<...> but does |
28 | /// not shrink in size. Once constructed, elements can be appended but cannot be |
29 | /// removed. The implementation is heavily dependent on the contract provided by |
30 | /// the Allocator type, in that all memory will be released when the Allocator |
31 | /// is destroyed. When an Array is destroyed, it will destroy elements in the |
32 | /// backing store but will not free the memory. |
33 | template <class T> class Array { |
34 | struct Segment { |
35 | Segment *Prev; |
36 | Segment *Next; |
37 | char Data[1]; |
38 | }; |
39 | |
40 | public: |
41 | // Each segment of the array will be laid out with the following assumptions: |
42 | // |
43 | // - Each segment will be on a cache-line address boundary (kCacheLineSize |
44 | // aligned). |
45 | // |
46 | // - The elements will be accessed through an aligned pointer, dependent on |
47 | // the alignment of T. |
48 | // |
49 | // - Each element is at least two-pointers worth from the beginning of the |
50 | // Segment, aligned properly, and the rest of the elements are accessed |
51 | // through appropriate alignment. |
52 | // |
53 | // We then compute the size of the segment to follow this logic: |
54 | // |
55 | // - Compute the number of elements that can fit within |
56 | // kCacheLineSize-multiple segments, minus the size of two pointers. |
57 | // |
58 | // - Request cacheline-multiple sized elements from the allocator. |
59 | static constexpr uint64_t AlignedElementStorageSize = sizeof(T); |
60 | |
61 | static constexpr uint64_t SegmentControlBlockSize = sizeof(Segment *) * 2; |
62 | |
63 | static constexpr uint64_t SegmentSize = nearest_boundary( |
64 | number: SegmentControlBlockSize + next_pow2(number: sizeof(T)), multiple: kCacheLineSize); |
65 | |
66 | using AllocatorType = Allocator<SegmentSize>; |
67 | |
68 | static constexpr uint64_t ElementsPerSegment = |
69 | (SegmentSize - SegmentControlBlockSize) / next_pow2(number: sizeof(T)); |
70 | |
71 | static_assert(ElementsPerSegment > 0, |
72 | "Must have at least 1 element per segment." ); |
73 | |
74 | static Segment SentinelSegment; |
75 | |
76 | using size_type = uint64_t; |
77 | |
78 | private: |
79 | // This Iterator models a BidirectionalIterator. |
80 | template <class U> class Iterator { |
81 | Segment *S = &SentinelSegment; |
82 | uint64_t Offset = 0; |
83 | uint64_t Size = 0; |
84 | |
85 | public: |
86 | Iterator(Segment *IS, uint64_t Off, uint64_t S) XRAY_NEVER_INSTRUMENT |
87 | : S(IS), |
88 | Offset(Off), |
89 | Size(S) {} |
90 | Iterator(const Iterator &) NOEXCEPT XRAY_NEVER_INSTRUMENT = default; |
91 | Iterator() NOEXCEPT XRAY_NEVER_INSTRUMENT = default; |
92 | Iterator(Iterator &&) NOEXCEPT XRAY_NEVER_INSTRUMENT = default; |
93 | Iterator &operator=(const Iterator &) XRAY_NEVER_INSTRUMENT = default; |
94 | Iterator &operator=(Iterator &&) XRAY_NEVER_INSTRUMENT = default; |
95 | ~Iterator() XRAY_NEVER_INSTRUMENT = default; |
96 | |
97 | Iterator &operator++() XRAY_NEVER_INSTRUMENT { |
98 | if (++Offset % ElementsPerSegment || Offset == Size) |
99 | return *this; |
100 | |
101 | // At this point, we know that Offset % N == 0, so we must advance the |
102 | // segment pointer. |
103 | DCHECK_EQ(Offset % ElementsPerSegment, 0); |
104 | DCHECK_NE(Offset, Size); |
105 | DCHECK_NE(S, &SentinelSegment); |
106 | DCHECK_NE(S->Next, &SentinelSegment); |
107 | S = S->Next; |
108 | DCHECK_NE(S, &SentinelSegment); |
109 | return *this; |
110 | } |
111 | |
112 | Iterator &operator--() XRAY_NEVER_INSTRUMENT { |
113 | DCHECK_NE(S, &SentinelSegment); |
114 | DCHECK_GT(Offset, 0); |
115 | |
116 | auto PreviousOffset = Offset--; |
117 | if (PreviousOffset != Size && PreviousOffset % ElementsPerSegment == 0) { |
118 | DCHECK_NE(S->Prev, &SentinelSegment); |
119 | S = S->Prev; |
120 | } |
121 | |
122 | return *this; |
123 | } |
124 | |
125 | Iterator operator++(int) XRAY_NEVER_INSTRUMENT { |
126 | Iterator Copy(*this); |
127 | ++(*this); |
128 | return Copy; |
129 | } |
130 | |
131 | Iterator operator--(int) XRAY_NEVER_INSTRUMENT { |
132 | Iterator Copy(*this); |
133 | --(*this); |
134 | return Copy; |
135 | } |
136 | |
137 | template <class V, class W> |
138 | friend bool operator==(const Iterator<V> &L, |
139 | const Iterator<W> &R) XRAY_NEVER_INSTRUMENT { |
140 | return L.S == R.S && L.Offset == R.Offset; |
141 | } |
142 | |
143 | template <class V, class W> |
144 | friend bool operator!=(const Iterator<V> &L, |
145 | const Iterator<W> &R) XRAY_NEVER_INSTRUMENT { |
146 | return !(L == R); |
147 | } |
148 | |
149 | U &operator*() const XRAY_NEVER_INSTRUMENT { |
150 | DCHECK_NE(S, &SentinelSegment); |
151 | auto RelOff = Offset % ElementsPerSegment; |
152 | |
153 | // We need to compute the character-aligned pointer, offset from the |
154 | // segment's Data location to get the element in the position of Offset. |
155 | auto Base = &S->Data; |
156 | auto AlignedOffset = Base + (RelOff * AlignedElementStorageSize); |
157 | return *reinterpret_cast<U *>(AlignedOffset); |
158 | } |
159 | |
160 | U *operator->() const XRAY_NEVER_INSTRUMENT { return &(**this); } |
161 | }; |
162 | |
163 | AllocatorType *Alloc; |
164 | Segment *Head; |
165 | Segment *Tail; |
166 | |
167 | // Here we keep track of segments in the freelist, to allow us to re-use |
168 | // segments when elements are trimmed off the end. |
169 | Segment *Freelist; |
170 | uint64_t Size; |
171 | |
172 | // =============================== |
173 | // In the following implementation, we work through the algorithms and the |
174 | // list operations using the following notation: |
175 | // |
176 | // - pred(s) is the predecessor (previous node accessor) and succ(s) is |
177 | // the successor (next node accessor). |
178 | // |
179 | // - S is a sentinel segment, which has the following property: |
180 | // |
181 | // pred(S) == succ(S) == S |
182 | // |
183 | // - @ is a loop operator, which can imply pred(s) == s if it appears on |
184 | // the left of s, or succ(s) == S if it appears on the right of s. |
185 | // |
186 | // - sL <-> sR : means a bidirectional relation between sL and sR, which |
187 | // means: |
188 | // |
189 | // succ(sL) == sR && pred(SR) == sL |
190 | // |
191 | // - sL -> sR : implies a unidirectional relation between sL and SR, |
192 | // with the following properties: |
193 | // |
194 | // succ(sL) == sR |
195 | // |
196 | // sL <- sR : implies a unidirectional relation between sR and sL, |
197 | // with the following properties: |
198 | // |
199 | // pred(sR) == sL |
200 | // |
201 | // =============================== |
202 | |
203 | Segment *NewSegment() XRAY_NEVER_INSTRUMENT { |
204 | // We need to handle the case in which enough elements have been trimmed to |
205 | // allow us to re-use segments we've allocated before. For this we look into |
206 | // the Freelist, to see whether we need to actually allocate new blocks or |
207 | // just re-use blocks we've already seen before. |
208 | if (Freelist != &SentinelSegment) { |
209 | // The current state of lists resemble something like this at this point: |
210 | // |
211 | // Freelist: @S@<-f0->...<->fN->@S@ |
212 | // ^ Freelist |
213 | // |
214 | // We want to perform a splice of `f0` from Freelist to a temporary list, |
215 | // which looks like: |
216 | // |
217 | // Templist: @S@<-f0->@S@ |
218 | // ^ FreeSegment |
219 | // |
220 | // Our algorithm preconditions are: |
221 | DCHECK_EQ(Freelist->Prev, &SentinelSegment); |
222 | |
223 | // Then the algorithm we implement is: |
224 | // |
225 | // SFS = Freelist |
226 | // Freelist = succ(Freelist) |
227 | // if (Freelist != S) |
228 | // pred(Freelist) = S |
229 | // succ(SFS) = S |
230 | // pred(SFS) = S |
231 | // |
232 | auto *FreeSegment = Freelist; |
233 | Freelist = Freelist->Next; |
234 | |
235 | // Note that we need to handle the case where Freelist is now pointing to |
236 | // S, which we don't want to be overwriting. |
237 | // TODO: Determine whether the cost of the branch is higher than the cost |
238 | // of the blind assignment. |
239 | if (Freelist != &SentinelSegment) |
240 | Freelist->Prev = &SentinelSegment; |
241 | |
242 | FreeSegment->Next = &SentinelSegment; |
243 | FreeSegment->Prev = &SentinelSegment; |
244 | |
245 | // Our postconditions are: |
246 | DCHECK_EQ(Freelist->Prev, &SentinelSegment); |
247 | DCHECK_NE(FreeSegment, &SentinelSegment); |
248 | return FreeSegment; |
249 | } |
250 | |
251 | auto SegmentBlock = Alloc->Allocate(); |
252 | if (SegmentBlock.Data == nullptr) |
253 | return nullptr; |
254 | |
255 | // Placement-new the Segment element at the beginning of the SegmentBlock. |
256 | new (SegmentBlock.Data) Segment{&SentinelSegment, &SentinelSegment, {0}}; |
257 | auto SB = reinterpret_cast<Segment *>(SegmentBlock.Data); |
258 | return SB; |
259 | } |
260 | |
261 | Segment *InitHeadAndTail() XRAY_NEVER_INSTRUMENT { |
262 | DCHECK_EQ(Head, &SentinelSegment); |
263 | DCHECK_EQ(Tail, &SentinelSegment); |
264 | auto S = NewSegment(); |
265 | if (S == nullptr) |
266 | return nullptr; |
267 | DCHECK_EQ(S->Next, &SentinelSegment); |
268 | DCHECK_EQ(S->Prev, &SentinelSegment); |
269 | DCHECK_NE(S, &SentinelSegment); |
270 | Head = S; |
271 | Tail = S; |
272 | DCHECK_EQ(Head, Tail); |
273 | DCHECK_EQ(Tail->Next, &SentinelSegment); |
274 | DCHECK_EQ(Tail->Prev, &SentinelSegment); |
275 | return S; |
276 | } |
277 | |
278 | Segment *AppendNewSegment() XRAY_NEVER_INSTRUMENT { |
279 | auto S = NewSegment(); |
280 | if (S == nullptr) |
281 | return nullptr; |
282 | DCHECK_NE(Tail, &SentinelSegment); |
283 | DCHECK_EQ(Tail->Next, &SentinelSegment); |
284 | DCHECK_EQ(S->Prev, &SentinelSegment); |
285 | DCHECK_EQ(S->Next, &SentinelSegment); |
286 | S->Prev = Tail; |
287 | Tail->Next = S; |
288 | Tail = S; |
289 | DCHECK_EQ(S, S->Prev->Next); |
290 | DCHECK_EQ(Tail->Next, &SentinelSegment); |
291 | return S; |
292 | } |
293 | |
294 | public: |
295 | explicit Array(AllocatorType &A) XRAY_NEVER_INSTRUMENT |
296 | : Alloc(&A), |
297 | Head(&SentinelSegment), |
298 | Tail(&SentinelSegment), |
299 | Freelist(&SentinelSegment), |
300 | Size(0) {} |
301 | |
302 | Array() XRAY_NEVER_INSTRUMENT : Alloc(nullptr), |
303 | Head(&SentinelSegment), |
304 | Tail(&SentinelSegment), |
305 | Freelist(&SentinelSegment), |
306 | Size(0) {} |
307 | |
308 | Array(const Array &) = delete; |
309 | Array &operator=(const Array &) = delete; |
310 | |
311 | Array(Array &&O) XRAY_NEVER_INSTRUMENT : Alloc(O.Alloc), |
312 | Head(O.Head), |
313 | Tail(O.Tail), |
314 | Freelist(O.Freelist), |
315 | Size(O.Size) { |
316 | O.Alloc = nullptr; |
317 | O.Head = &SentinelSegment; |
318 | O.Tail = &SentinelSegment; |
319 | O.Size = 0; |
320 | O.Freelist = &SentinelSegment; |
321 | } |
322 | |
323 | Array &operator=(Array &&O) XRAY_NEVER_INSTRUMENT { |
324 | Alloc = O.Alloc; |
325 | O.Alloc = nullptr; |
326 | Head = O.Head; |
327 | O.Head = &SentinelSegment; |
328 | Tail = O.Tail; |
329 | O.Tail = &SentinelSegment; |
330 | Freelist = O.Freelist; |
331 | O.Freelist = &SentinelSegment; |
332 | Size = O.Size; |
333 | O.Size = 0; |
334 | return *this; |
335 | } |
336 | |
337 | ~Array() XRAY_NEVER_INSTRUMENT { |
338 | for (auto &E : *this) |
339 | (&E)->~T(); |
340 | } |
341 | |
342 | bool empty() const XRAY_NEVER_INSTRUMENT { return Size == 0; } |
343 | |
344 | AllocatorType &allocator() const XRAY_NEVER_INSTRUMENT { |
345 | DCHECK_NE(Alloc, nullptr); |
346 | return *Alloc; |
347 | } |
348 | |
349 | uint64_t size() const XRAY_NEVER_INSTRUMENT { return Size; } |
350 | |
351 | template <class... Args> |
352 | T *AppendEmplace(Args &&... args) XRAY_NEVER_INSTRUMENT { |
353 | DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) || |
354 | (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment)); |
355 | if (UNLIKELY(Head == &SentinelSegment)) { |
356 | auto R = InitHeadAndTail(); |
357 | if (R == nullptr) |
358 | return nullptr; |
359 | } |
360 | |
361 | DCHECK_NE(Head, &SentinelSegment); |
362 | DCHECK_NE(Tail, &SentinelSegment); |
363 | |
364 | auto Offset = Size % ElementsPerSegment; |
365 | if (UNLIKELY(Size != 0 && Offset == 0)) |
366 | if (AppendNewSegment() == nullptr) |
367 | return nullptr; |
368 | |
369 | DCHECK_NE(Tail, &SentinelSegment); |
370 | auto Base = &Tail->Data; |
371 | auto AlignedOffset = Base + (Offset * AlignedElementStorageSize); |
372 | DCHECK_LE(AlignedOffset + sizeof(T), |
373 | reinterpret_cast<unsigned char *>(Base) + SegmentSize); |
374 | |
375 | // In-place construct at Position. |
376 | new (AlignedOffset) T{std::forward<Args>(args)...}; |
377 | ++Size; |
378 | return reinterpret_cast<T *>(AlignedOffset); |
379 | } |
380 | |
381 | T *Append(const T &E) XRAY_NEVER_INSTRUMENT { |
382 | // FIXME: This is a duplication of AppenEmplace with the copy semantics |
383 | // explicitly used, as a work-around to GCC 4.8 not invoking the copy |
384 | // constructor with the placement new with braced-init syntax. |
385 | DCHECK((Size == 0 && Head == &SentinelSegment && Head == Tail) || |
386 | (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment)); |
387 | if (UNLIKELY(Head == &SentinelSegment)) { |
388 | auto R = InitHeadAndTail(); |
389 | if (R == nullptr) |
390 | return nullptr; |
391 | } |
392 | |
393 | DCHECK_NE(Head, &SentinelSegment); |
394 | DCHECK_NE(Tail, &SentinelSegment); |
395 | |
396 | auto Offset = Size % ElementsPerSegment; |
397 | if (UNLIKELY(Size != 0 && Offset == 0)) |
398 | if (AppendNewSegment() == nullptr) |
399 | return nullptr; |
400 | |
401 | DCHECK_NE(Tail, &SentinelSegment); |
402 | auto Base = &Tail->Data; |
403 | auto AlignedOffset = Base + (Offset * AlignedElementStorageSize); |
404 | DCHECK_LE(AlignedOffset + sizeof(T), |
405 | reinterpret_cast<unsigned char *>(Tail) + SegmentSize); |
406 | |
407 | // In-place construct at Position. |
408 | new (AlignedOffset) T(E); |
409 | ++Size; |
410 | return reinterpret_cast<T *>(AlignedOffset); |
411 | } |
412 | |
413 | T &operator[](uint64_t Offset) const XRAY_NEVER_INSTRUMENT { |
414 | DCHECK_LE(Offset, Size); |
415 | // We need to traverse the array enough times to find the element at Offset. |
416 | auto S = Head; |
417 | while (Offset >= ElementsPerSegment) { |
418 | S = S->Next; |
419 | Offset -= ElementsPerSegment; |
420 | DCHECK_NE(S, &SentinelSegment); |
421 | } |
422 | auto Base = &S->Data; |
423 | auto AlignedOffset = Base + (Offset * AlignedElementStorageSize); |
424 | auto Position = reinterpret_cast<T *>(AlignedOffset); |
425 | return *reinterpret_cast<T *>(Position); |
426 | } |
427 | |
428 | T &front() const XRAY_NEVER_INSTRUMENT { |
429 | DCHECK_NE(Head, &SentinelSegment); |
430 | DCHECK_NE(Size, 0u); |
431 | return *begin(); |
432 | } |
433 | |
434 | T &back() const XRAY_NEVER_INSTRUMENT { |
435 | DCHECK_NE(Tail, &SentinelSegment); |
436 | DCHECK_NE(Size, 0u); |
437 | auto It = end(); |
438 | --It; |
439 | return *It; |
440 | } |
441 | |
442 | template <class Predicate> |
443 | T *find_element(Predicate P) const XRAY_NEVER_INSTRUMENT { |
444 | if (empty()) |
445 | return nullptr; |
446 | |
447 | auto E = end(); |
448 | for (auto I = begin(); I != E; ++I) |
449 | if (P(*I)) |
450 | return &(*I); |
451 | |
452 | return nullptr; |
453 | } |
454 | |
455 | /// Remove N Elements from the end. This leaves the blocks behind, and not |
456 | /// require allocation of new blocks for new elements added after trimming. |
457 | void trim(uint64_t Elements) XRAY_NEVER_INSTRUMENT { |
458 | auto OldSize = Size; |
459 | Elements = Elements > Size ? Size : Elements; |
460 | Size -= Elements; |
461 | |
462 | // We compute the number of segments we're going to return from the tail by |
463 | // counting how many elements have been trimmed. Given the following: |
464 | // |
465 | // - Each segment has N valid positions, where N > 0 |
466 | // - The previous size > current size |
467 | // |
468 | // To compute the number of segments to return, we need to perform the |
469 | // following calculations for the number of segments required given 'x' |
470 | // elements: |
471 | // |
472 | // f(x) = { |
473 | // x == 0 : 0 |
474 | // , 0 < x <= N : 1 |
475 | // , N < x <= max : x / N + (x % N ? 1 : 0) |
476 | // } |
477 | // |
478 | // We can simplify this down to: |
479 | // |
480 | // f(x) = { |
481 | // x == 0 : 0, |
482 | // , 0 < x <= max : x / N + (x < N || x % N ? 1 : 0) |
483 | // } |
484 | // |
485 | // And further down to: |
486 | // |
487 | // f(x) = x ? x / N + (x < N || x % N ? 1 : 0) : 0 |
488 | // |
489 | // We can then perform the following calculation `s` which counts the number |
490 | // of segments we need to remove from the end of the data structure: |
491 | // |
492 | // s(p, c) = f(p) - f(c) |
493 | // |
494 | // If we treat p = previous size, and c = current size, and given the |
495 | // properties above, the possible range for s(...) is [0..max(typeof(p))/N] |
496 | // given that typeof(p) == typeof(c). |
497 | auto F = [](uint64_t X) { |
498 | return X ? (X / ElementsPerSegment) + |
499 | (X < ElementsPerSegment || X % ElementsPerSegment ? 1 : 0) |
500 | : 0; |
501 | }; |
502 | auto PS = F(OldSize); |
503 | auto CS = F(Size); |
504 | DCHECK_GE(PS, CS); |
505 | auto SegmentsToTrim = PS - CS; |
506 | for (auto I = 0uL; I < SegmentsToTrim; ++I) { |
507 | // Here we place the current tail segment to the freelist. To do this |
508 | // appropriately, we need to perform a splice operation on two |
509 | // bidirectional linked-lists. In particular, we have the current state of |
510 | // the doubly-linked list of segments: |
511 | // |
512 | // @S@ <- s0 <-> s1 <-> ... <-> sT -> @S@ |
513 | // |
514 | DCHECK_NE(Head, &SentinelSegment); |
515 | DCHECK_NE(Tail, &SentinelSegment); |
516 | DCHECK_EQ(Tail->Next, &SentinelSegment); |
517 | |
518 | if (Freelist == &SentinelSegment) { |
519 | // Our two lists at this point are in this configuration: |
520 | // |
521 | // Freelist: (potentially) @S@ |
522 | // Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@ |
523 | // ^ Head ^ Tail |
524 | // |
525 | // The end state for us will be this configuration: |
526 | // |
527 | // Freelist: @S@<-sT->@S@ |
528 | // Mainlist: @S@<-s0<->s1<->...<->sPT->@S@ |
529 | // ^ Head ^ Tail |
530 | // |
531 | // The first step for us is to hold a reference to the tail of Mainlist, |
532 | // which in our notation is represented by sT. We call this our "free |
533 | // segment" which is the segment we are placing on the Freelist. |
534 | // |
535 | // sF = sT |
536 | // |
537 | // Then, we also hold a reference to the "pre-tail" element, which we |
538 | // call sPT: |
539 | // |
540 | // sPT = pred(sT) |
541 | // |
542 | // We want to splice sT into the beginning of the Freelist, which in |
543 | // an empty Freelist means placing a segment whose predecessor and |
544 | // successor is the sentinel segment. |
545 | // |
546 | // The splice operation then can be performed in the following |
547 | // algorithm: |
548 | // |
549 | // succ(sPT) = S |
550 | // pred(sT) = S |
551 | // succ(sT) = Freelist |
552 | // Freelist = sT |
553 | // Tail = sPT |
554 | // |
555 | auto SPT = Tail->Prev; |
556 | SPT->Next = &SentinelSegment; |
557 | Tail->Prev = &SentinelSegment; |
558 | Tail->Next = Freelist; |
559 | Freelist = Tail; |
560 | Tail = SPT; |
561 | |
562 | // Our post-conditions here are: |
563 | DCHECK_EQ(Tail->Next, &SentinelSegment); |
564 | DCHECK_EQ(Freelist->Prev, &SentinelSegment); |
565 | } else { |
566 | // In the other case, where the Freelist is not empty, we perform the |
567 | // following transformation instead: |
568 | // |
569 | // This transforms the current state: |
570 | // |
571 | // Freelist: @S@<-f0->@S@ |
572 | // ^ Freelist |
573 | // Mainlist: @S@<-s0<->s1<->...<->sPT<->sT->@S@ |
574 | // ^ Head ^ Tail |
575 | // |
576 | // Into the following: |
577 | // |
578 | // Freelist: @S@<-sT<->f0->@S@ |
579 | // ^ Freelist |
580 | // Mainlist: @S@<-s0<->s1<->...<->sPT->@S@ |
581 | // ^ Head ^ Tail |
582 | // |
583 | // The algorithm is: |
584 | // |
585 | // sFH = Freelist |
586 | // sPT = pred(sT) |
587 | // pred(SFH) = sT |
588 | // succ(sT) = Freelist |
589 | // pred(sT) = S |
590 | // succ(sPT) = S |
591 | // Tail = sPT |
592 | // Freelist = sT |
593 | // |
594 | auto SFH = Freelist; |
595 | auto SPT = Tail->Prev; |
596 | auto ST = Tail; |
597 | SFH->Prev = ST; |
598 | ST->Next = Freelist; |
599 | ST->Prev = &SentinelSegment; |
600 | SPT->Next = &SentinelSegment; |
601 | Tail = SPT; |
602 | Freelist = ST; |
603 | |
604 | // Our post-conditions here are: |
605 | DCHECK_EQ(Tail->Next, &SentinelSegment); |
606 | DCHECK_EQ(Freelist->Prev, &SentinelSegment); |
607 | DCHECK_EQ(Freelist->Next->Prev, Freelist); |
608 | } |
609 | } |
610 | |
611 | // Now in case we've spliced all the segments in the end, we ensure that the |
612 | // main list is "empty", or both the head and tail pointing to the sentinel |
613 | // segment. |
614 | if (Tail == &SentinelSegment) |
615 | Head = Tail; |
616 | |
617 | DCHECK( |
618 | (Size == 0 && Head == &SentinelSegment && Tail == &SentinelSegment) || |
619 | (Size != 0 && Head != &SentinelSegment && Tail != &SentinelSegment)); |
620 | DCHECK( |
621 | (Freelist != &SentinelSegment && Freelist->Prev == &SentinelSegment) || |
622 | (Freelist == &SentinelSegment && Tail->Next == &SentinelSegment)); |
623 | } |
624 | |
625 | // Provide iterators. |
626 | Iterator<T> begin() const XRAY_NEVER_INSTRUMENT { |
627 | return Iterator<T>(Head, 0, Size); |
628 | } |
629 | Iterator<T> end() const XRAY_NEVER_INSTRUMENT { |
630 | return Iterator<T>(Tail, Size, Size); |
631 | } |
632 | Iterator<const T> cbegin() const XRAY_NEVER_INSTRUMENT { |
633 | return Iterator<const T>(Head, 0, Size); |
634 | } |
635 | Iterator<const T> cend() const XRAY_NEVER_INSTRUMENT { |
636 | return Iterator<const T>(Tail, Size, Size); |
637 | } |
638 | }; |
639 | |
640 | // We need to have this storage definition out-of-line so that the compiler can |
641 | // ensure that storage for the SentinelSegment is defined and has a single |
642 | // address. |
643 | template <class T> |
644 | typename Array<T>::Segment Array<T>::SentinelSegment{ |
645 | &Array<T>::SentinelSegment, &Array<T>::SentinelSegment, {'\0'}}; |
646 | |
647 | } // namespace __xray |
648 | |
649 | #endif // XRAY_SEGMENTED_ARRAY_H |
650 | |