TPDE
Loading...
Searching...
No Matches
SegmentedVector.hpp
1// SPDX-FileCopyrightText: 2025 Contributors to TPDE <https://tpde.org>
2// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
3#pragma once
4
5#include "tpde/util/AddressSanitizer.hpp"
6#include "tpde/util/SmallVector.hpp"
7
8#include <cstddef>
9#include <memory>
10
11namespace tpde::util {
12
13/// A vector with stable element addresses.
14template <typename T, size_t SegmentSize = 64>
15class SegmentedVector {
16public:
17 using value_type = T;
18
19private:
20 T *cur = nullptr;
21 T *end = nullptr;
22 SmallVector<T *> segments;
23 unsigned used_segments = 0;
24 std::allocator<T> allocator;
25
26public:
27 SegmentedVector() = default;
28 ~SegmentedVector() {
29 clear();
30 deallocate_segments();
31 }
32
33 SegmentedVector(const SegmentedVector &) = delete;
34
35 SegmentedVector &operator=(const SegmentedVector &) = delete;
36
37 /// Get the number of elements in the vector.
38 size_t size() const { return used_segments * SegmentSize - (end - cur); }
39
40 const T &operator[](size_t idx) const {
41 assert(idx < size());
42 return segments[idx / SegmentSize][idx % SegmentSize];
43 }
44
45 T &operator[](size_t idx) {
46 assert(idx < size());
47 return segments[idx / SegmentSize][idx % SegmentSize];
48 }
49
50 void clear() {
51 if (used_segments == 0) {
52 assert(cur == nullptr && end == nullptr && segments.empty());
53 return;
54 }
55 for (unsigned i = 0; i < used_segments - 1; ++i) {
56 std::destroy(segments[i], segments[i] + SegmentSize);
57 util::poison_memory_region(segments[i], SegmentSize * sizeof(T));
58 }
59 T *last_start = segments[used_segments - 1];
60 assert(last_start + SegmentSize == end);
61 assert(last_start <= cur && cur <= end);
62 std::destroy(last_start, cur);
63 util::poison_memory_region(last_start, SegmentSize * sizeof(T));
64
65 used_segments = 1;
66 cur = segments[0];
67 end = segments[0] + SegmentSize;
68 }
69
70 void push_back(const T &value) { std::construct_at(allocate(), value); }
71
72 void push_back(T &&value) { std::construct_at(allocate(), std::move(value)); }
73
74 template <typename... Args>
75 T &emplace_back(Args &&...args) {
76 return *std::construct_at(allocate(), std::forward<Args>(args)...);
77 }
78
79private:
80 T *allocate() {
81 if (cur == end) [[unlikely]] {
82 next_segment();
83 }
84 util::unpoison_memory_region(cur, sizeof(T));
85 return cur++;
86 }
87
88 void next_segment() {
89 assert(cur == end);
90 if (used_segments == segments.size()) {
91 T *segment = allocator.allocate(SegmentSize);
92 util::poison_memory_region(segment, SegmentSize * sizeof(T));
93 segments.push_back(segment);
94 }
95 cur = segments[used_segments++];
96 end = cur + SegmentSize;
97 }
98
99 void deallocate_segments() {
100 for (T *segment : segments) {
101 allocator.deallocate(segment, SegmentSize);
102 }
103 }
104};
105
106} // namespace tpde::util
size_t size() const
Get the number of elements in the vector.