summaryrefslogtreecommitdiff
path: root/src/common
diff options
context:
space:
mode:
Diffstat (limited to 'src/common')
-rw-r--r--src/common/CMakeLists.txt1
-rw-r--r--src/common/slot_vector.h227
2 files changed, 228 insertions, 0 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 85926fc8f..bf3f3b781 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -121,6 +121,7 @@ add_library(common STATIC
121 settings_input.cpp 121 settings_input.cpp
122 settings_input.h 122 settings_input.h
123 settings_setting.h 123 settings_setting.h
124 slot_vector.h
124 socket_types.h 125 socket_types.h
125 spin_lock.cpp 126 spin_lock.cpp
126 spin_lock.h 127 spin_lock.h
diff --git a/src/common/slot_vector.h b/src/common/slot_vector.h
new file mode 100644
index 000000000..34ff7de94
--- /dev/null
+++ b/src/common/slot_vector.h
@@ -0,0 +1,227 @@
1// SPDX-FileCopyrightText: Copyright 2020 yuzu Emulator Project
2// SPDX-License-Identifier: GPL-2.0-or-later
3
4#pragma once
5
6#include <algorithm>
7#include <bit>
8#include <numeric>
9#include <type_traits>
10#include <utility>
11#include <vector>
12
13#include "common/assert.h"
14#include "common/common_types.h"
15#include "common/polyfill_ranges.h"
16
17namespace Common {
18
19struct SlotId {
20 static constexpr u32 INVALID_INDEX = std::numeric_limits<u32>::max();
21
22 constexpr auto operator<=>(const SlotId&) const noexcept = default;
23
24 constexpr explicit operator bool() const noexcept {
25 return index != INVALID_INDEX;
26 }
27
28 u32 index = INVALID_INDEX;
29};
30
31template <class T>
32 requires std::is_nothrow_move_assignable_v<T> && std::is_nothrow_move_constructible_v<T>
33class SlotVector {
34public:
35 class Iterator {
36 friend SlotVector<T>;
37
38 public:
39 constexpr Iterator() = default;
40
41 Iterator& operator++() noexcept {
42 const u64* const bitset = slot_vector->stored_bitset.data();
43 const u32 size = static_cast<u32>(slot_vector->stored_bitset.size()) * 64;
44 if (id.index < size) {
45 do {
46 ++id.index;
47 } while (id.index < size && !IsValid(bitset));
48 if (id.index == size) {
49 id.index = SlotId::INVALID_INDEX;
50 }
51 }
52 return *this;
53 }
54
55 Iterator operator++(int) noexcept {
56 const Iterator copy{*this};
57 ++*this;
58 return copy;
59 }
60
61 bool operator==(const Iterator& other) const noexcept {
62 return id.index == other.id.index;
63 }
64
65 bool operator!=(const Iterator& other) const noexcept {
66 return id.index != other.id.index;
67 }
68
69 std::pair<SlotId, T*> operator*() const noexcept {
70 return {id, std::addressof((*slot_vector)[id])};
71 }
72
73 T* operator->() const noexcept {
74 return std::addressof((*slot_vector)[id]);
75 }
76
77 private:
78 Iterator(SlotVector<T>* slot_vector_, SlotId id_) noexcept
79 : slot_vector{slot_vector_}, id{id_} {}
80
81 bool IsValid(const u64* bitset) const noexcept {
82 return ((bitset[id.index / 64] >> (id.index % 64)) & 1) != 0;
83 }
84
85 SlotVector<T>* slot_vector;
86 SlotId id;
87 };
88
89 ~SlotVector() noexcept {
90 size_t index = 0;
91 for (u64 bits : stored_bitset) {
92 for (size_t bit = 0; bits; ++bit, bits >>= 1) {
93 if ((bits & 1) != 0) {
94 values[index + bit].object.~T();
95 }
96 }
97 index += 64;
98 }
99 delete[] values;
100 }
101
102 [[nodiscard]] T& operator[](SlotId id) noexcept {
103 ValidateIndex(id);
104 return values[id.index].object;
105 }
106
107 [[nodiscard]] const T& operator[](SlotId id) const noexcept {
108 ValidateIndex(id);
109 return values[id.index].object;
110 }
111
112 template <typename... Args>
113 [[nodiscard]] SlotId insert(Args&&... args) noexcept {
114 const u32 index = FreeValueIndex();
115 new (&values[index].object) T(std::forward<Args>(args)...);
116 SetStorageBit(index);
117
118 return SlotId{index};
119 }
120
121 void erase(SlotId id) noexcept {
122 values[id.index].object.~T();
123 free_list.push_back(id.index);
124 ResetStorageBit(id.index);
125 }
126
127 [[nodiscard]] Iterator begin() noexcept {
128 const auto it = std::ranges::find_if(stored_bitset, [](u64 value) { return value != 0; });
129 if (it == stored_bitset.end()) {
130 return end();
131 }
132 const u32 word_index = static_cast<u32>(std::distance(it, stored_bitset.begin()));
133 const SlotId first_id{word_index * 64 + static_cast<u32>(std::countr_zero(*it))};
134 return Iterator(this, first_id);
135 }
136
137 [[nodiscard]] Iterator end() noexcept {
138 return Iterator(this, SlotId{SlotId::INVALID_INDEX});
139 }
140
141 [[nodiscard]] size_t size() const noexcept {
142 return values_capacity - free_list.size();
143 }
144
145private:
146 struct NonTrivialDummy {
147 NonTrivialDummy() noexcept {}
148 };
149
150 union Entry {
151 Entry() noexcept : dummy{} {}
152 ~Entry() noexcept {}
153
154 NonTrivialDummy dummy;
155 T object;
156 };
157
158 void SetStorageBit(u32 index) noexcept {
159 stored_bitset[index / 64] |= u64(1) << (index % 64);
160 }
161
162 void ResetStorageBit(u32 index) noexcept {
163 stored_bitset[index / 64] &= ~(u64(1) << (index % 64));
164 }
165
166 bool ReadStorageBit(u32 index) noexcept {
167 return ((stored_bitset[index / 64] >> (index % 64)) & 1) != 0;
168 }
169
170 void ValidateIndex(SlotId id) const noexcept {
171 DEBUG_ASSERT(id);
172 DEBUG_ASSERT(id.index / 64 < stored_bitset.size());
173 DEBUG_ASSERT(((stored_bitset[id.index / 64] >> (id.index % 64)) & 1) != 0);
174 }
175
176 [[nodiscard]] u32 FreeValueIndex() noexcept {
177 if (free_list.empty()) {
178 Reserve(values_capacity ? (values_capacity << 1) : 1);
179 }
180 const u32 free_index = free_list.back();
181 free_list.pop_back();
182 return free_index;
183 }
184
185 void Reserve(size_t new_capacity) noexcept {
186 Entry* const new_values = new Entry[new_capacity];
187 size_t index = 0;
188 for (u64 bits : stored_bitset) {
189 for (size_t bit = 0; bits; ++bit, bits >>= 1) {
190 const size_t i = index + bit;
191 if ((bits & 1) == 0) {
192 continue;
193 }
194 T& old_value = values[i].object;
195 new (&new_values[i].object) T(std::move(old_value));
196 old_value.~T();
197 }
198 index += 64;
199 }
200
201 stored_bitset.resize((new_capacity + 63) / 64);
202
203 const size_t old_free_size = free_list.size();
204 free_list.resize(old_free_size + (new_capacity - values_capacity));
205 std::iota(free_list.begin() + old_free_size, free_list.end(),
206 static_cast<u32>(values_capacity));
207
208 delete[] values;
209 values = new_values;
210 values_capacity = new_capacity;
211 }
212
213 Entry* values = nullptr;
214 size_t values_capacity = 0;
215
216 std::vector<u64> stored_bitset;
217 std::vector<u32> free_list;
218};
219
220} // namespace Common
221
222template <>
223struct std::hash<Common::SlotId> {
224 size_t operator()(const Common::SlotId& id) const noexcept {
225 return std::hash<u32>{}(id.index);
226 }
227};