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>
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() noexcept {
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 noexcept {
39 return used_segments * SegmentSize - (end - cur);
40 }
41
42 const T &operator[](size_t idx) const noexcept {
43 assert(idx < size());
44 return segments[idx / SegmentSize][idx % SegmentSize];
45 }
46
47 T &operator[](size_t idx) noexcept {
48 assert(idx < size());
49 return segments[idx / SegmentSize][idx % SegmentSize];
50 }
51
52 void clear() noexcept {
53 if (used_segments == 0) {
54 assert(cur == nullptr && end == nullptr && segments.empty());
55 return;
56 }
57 for (unsigned i = 0; i < used_segments - 1; ++i) {
58 std::destroy(segments[i], segments[i] + SegmentSize);
59 util::poison_memory_region(segments[i], SegmentSize * sizeof(T));
60 }
61 T *last_start = segments[used_segments - 1];
62 assert(last_start + SegmentSize == end);
63 assert(last_start <= cur && cur <= end);
64 std::destroy(last_start, cur);
65 util::poison_memory_region(last_start, SegmentSize * sizeof(T));
66
67 used_segments = 1;
68 cur = segments[0];
69 end = segments[0] + SegmentSize;
70 }
71
72 void push_back(const T &value) noexcept {
73 std::construct_at(allocate(), value);
74 }
75
76 void push_back(T &&value) noexcept {
77 std::construct_at(allocate(), std::move(value));
78 }
79
80 template <typename... Args>
81 T &emplace_back(Args &&...args) noexcept {
82 return *std::construct_at(allocate(), std::forward<Args>(args)...);
83 }
84
85private:
86 T *allocate() noexcept {
87 if (cur == end) [[unlikely]] {
88 next_segment();
89 }
90 util::unpoison_memory_region(cur, sizeof(T));
91 return cur++;
92 }
93
94 void next_segment() noexcept {
95 assert(cur == end);
96 if (used_segments == segments.size()) {
97 T *segment = allocator.allocate(SegmentSize);
98 util::poison_memory_region(segment, SegmentSize * sizeof(T));
99 segments.push_back(segment);
100 }
101 cur = segments[used_segments++];
102 end = cur + SegmentSize;
103 }
104
105 void deallocate_segments() noexcept {
106 for (T *segment : segments) {
107 allocator.deallocate(segment, SegmentSize);
108 }
109 }
110};
111
112} // namespace tpde::util
A vector with stable element addresses.
size_t size() const noexcept
Get the number of elements in the vector.