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