DiscordCoreAPI
A Discord bot library written in C++, with custom asynchronous coroutines.
Loading...
Searching...
No Matches
UnorderedSet.hpp
Go to the documentation of this file.
1/*
2 MIT License
3
4 DiscordCoreAPI, A bot library for Discord, written in C++, and featuring explicit multithreading through the usage of custom, asynchronous C++ CoRoutines.
5
6 Copyright 2022, 2023 Chris M. (RealTimeChris)
7
8 Permission is hereby granted, free of charge, to any person obtaining a copy
9 of this software and associated documentation files (the "Software"), to deal
10 in the Software without restriction, including without limitation the rights
11 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 copies of the Software, and to permit persons to whom the Software is
13 furnished to do so, subject to the following conditions:
14
15 The above copyright notice and this permission notice shall be included in all
16 copies or substantial portions of the Software.
17
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 SOFTWARE.
25*/
26/// UnorderedSet.hpp - Header file for the UnorderedSet class.
27/// May 12, 2021
28/// https://discordcoreapi.com
29/// \file UnorderedSet.hpp
30
31#pragma once
32
34
35namespace DiscordCoreAPI {
36
37 template<typename ValueType> class UnorderedSet;
38
39 template<typename SetIterator, typename ValueType>
40 concept SetContainerIteratorT = std::is_same_v<typename UnorderedSet<ValueType>::iterator, std::decay_t<SetIterator>>;
41
42 template<typename ValueType>
43 class UnorderedSet : protected HashPolicy<UnorderedSet<ValueType>>, protected jsonifier_internal::alloc_wrapper<ValueType>, protected ObjectCompare, protected KeyAccessor {
44 public:
45 using mapped_type = ValueType;
46 using key_type = mapped_type;
47 using reference = mapped_type&;
48 using value_type = mapped_type;
49 using const_reference = const mapped_type&;
50 using size_type = uint64_t;
51 using object_compare = ObjectCompare;
52 using key_accessor = KeyAccessor;
53 using hash_policy = HashPolicy<UnorderedSet<mapped_type>>;
54 using key_hasher = hash_policy;
55 using iterator = HashIterator<UnorderedSet<mapped_type>>;
56 using const_iterator = HashIterator<const UnorderedSet<mapped_type>>;
57 using allocator = jsonifier_internal::alloc_wrapper<value_type>;
58
59 friend hash_policy;
60 friend iterator;
61 friend const_iterator;
62
63 inline UnorderedSet(){};
64
65 inline UnorderedSet& operator=(UnorderedSet&& other) noexcept {
66 if (this != &other) {
67 reset();
68 swap(other);
69 }
70 return *this;
71 }
72
73 inline UnorderedSet(UnorderedSet&& other) noexcept {
74 *this = std::move(other);
75 }
76
77 inline UnorderedSet& operator=(const UnorderedSet& other) {
78 if (this != &other) {
79 reset();
80
81 reserve(other.capacity());
82 for (const auto& value: other) {
83 emplace(value);
84 }
85 }
86 return *this;
87 }
88
89 inline UnorderedSet(const UnorderedSet& other) {
90 *this = other;
91 }
92
93 inline UnorderedSet(std::initializer_list<value_type> list) {
94 reserve(list.size());
95 for (auto& value: list) {
96 emplace(std::move(value));
97 }
98 };
99
100 template<typename Args> iterator emplace(Args&& value) {
101 return emplaceInternal(std::forward<Args>(value));
102 }
103
104 template<typename key_type_new> inline const_iterator find(key_type_new&& key) const {
105 if (sizeVal > 0) {
106 auto currentIndex = getKeyAccessor()(key) & (capacityVal - 1);
107 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
108 if (sentinelVector[currentIndex] > 0 && object_compare()(getKeyAccessor()(data[currentIndex]), getKeyAccessor()(key))) {
109 return { this, currentIndex };
110 }
111 }
112 }
113 return end();
114 }
115
116 template<typename key_type_new> inline iterator find(key_type_new&& key) {
117 if (sizeVal > 0) {
118 auto currentIndex = getKeyAccessor()(key) & (capacityVal - 1);
119 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
120 if (sentinelVector[currentIndex] > 0 && object_compare()(getKeyAccessor()(data[currentIndex]), getKeyAccessor()(key))) {
121 return { this, currentIndex };
122 }
123 }
124 }
125 return end();
126 }
127
128 template<typename key_type_new> inline const_reference operator[](key_type_new&& key) const {
129 auto iter = find(std::forward<key_type_new>(key));
130 if (iter == end()) {
131 iter = emplace(mapped_type{});
132 }
133 return *iter;
134 }
135
136 template<typename key_type_new> inline reference operator[](key_type_new&& key) {
137 auto iter = find(std::forward<key_type_new>(key));
138 if (iter == end()) {
139 iter = emplace(mapped_type{});
140 }
141 return *iter;
142 }
143
144 template<typename key_type_new> inline const_reference at(key_type_new&& key) const {
145 auto iter = find(std::forward<key_type_new>(key));
146 if (iter == end()) {
147 throw DCAException{ "Sorry, but an object by that key doesn't exist in this map." };
148 }
149 return *iter;
150 }
151
152 template<typename key_type_new> inline reference at(key_type_new&& key) {
153 auto iter = find(std::forward<key_type_new>(key));
154 if (iter == end()) {
155 throw DCAException{ "Sorry, but an object by that key doesn't exist in this map." };
156 }
157 return *iter;
158 }
159
160 template<typename key_type_new> inline bool contains(key_type_new&& key) const {
161 if (sizeVal > 0) {
162 auto currentIndex = getKeyAccessor()(key) & (capacityVal - 1);
163 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
164 if (sentinelVector[currentIndex] > 0 && object_compare()(getKeyAccessor().operator()(data[currentIndex]), getKeyAccessor().operator()(key))) {
165 return true;
166 }
167 }
168 }
169 return false;
170 }
171
172 template<SetContainerIteratorT<mapped_type> SetIterator> inline iterator erase(SetIterator&& iter) {
173 if (sizeVal > 0) {
174 auto currentIndex = static_cast<size_type>(iter.getRawPtr() - data);
175 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
176 if (sentinelVector[currentIndex] > 0 && object_compare()(getKeyAccessor()(data[currentIndex]), getKeyAccessor()(iter.operator*()))) {
177 sentinelVector[currentIndex] = 0;
178 sizeVal--;
179 return { this, ++currentIndex };
180 }
181 }
182 }
183 return end();
184 }
185
186 template<typename key_type_new> inline iterator erase(key_type_new&& key) {
187 if (sizeVal > 0) {
188 auto currentIndex = getKeyAccessor()(key) & (capacityVal - 1);
189 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
190 if (sentinelVector[currentIndex] > 0 && object_compare()(getKeyAccessor()(data[currentIndex]), getKeyAccessor()(key))) {
191 sentinelVector[currentIndex] = 0;
192 sizeVal--;
193 return { this, ++currentIndex };
194 }
195 }
196 }
197 return end();
198 }
199
200 inline const_iterator begin() const {
201 for (size_type x{ 0 }; x < capacityVal; ++x) {
202 if (sentinelVector[x] > 0) {
203 return { this, x };
204 }
205 }
206 return end();
207 }
208
209 inline const_iterator end() const {
210 return {};
211 }
212
213 inline iterator begin() {
214 for (size_type x{ 0 }; x < capacityVal; ++x) {
215 if (sentinelVector[x] > 0) {
216 return { this, x };
217 }
218 }
219 return end();
220 }
221
222 inline iterator end() {
223 return {};
224 }
225
226 inline bool full() const {
227 return static_cast<float>(sizeVal) >= static_cast<float>(capacityVal) * 0.90f;
228 }
229
230 inline size_type size() const {
231 return sizeVal;
232 }
233
234 inline bool empty() const {
235 return sizeVal == 0;
236 }
237
238 inline void reserve(size_type sizeNew) {
239 resize(sizeNew);
240 }
241
242 inline void swap(UnorderedSet& other) noexcept {
243 std::swap(maxLookAheadDistance, other.maxLookAheadDistance);
244 std::swap(sentinelVector, other.sentinelVector);
245 std::swap(capacityVal, other.capacityVal);
246 std::swap(sizeVal, other.sizeVal);
247 std::swap(data, other.data);
248 }
249
250 inline size_type capacity() const {
251 return capacityVal;
252 }
253
254 inline bool operator==(const UnorderedSet& other) const {
255 if (capacityVal != other.capacityVal || sizeVal != other.sizeVal || data != other.data) {
256 return false;
257 }
258 for (auto iter01{ begin() }, iter02{ other.begin() }; iter01 != end(); ++iter01, ++iter02) {
259 if (!object_compare()(iter01.operator*(), iter02.operator*())) {
260 return false;
261 }
262 }
263 return true;
264 }
265
266 inline void clear() {
267 for (size_type x = 0; x < sentinelVector.size(); ++x) {
268 if (sentinelVector[x] > 0) {
269 getAlloc().destroy(data + x);
270 sentinelVector[x] = 0;
271 }
272 }
273 sizeVal = 0;
274 }
275
276 inline ~UnorderedSet() {
277 reset();
278 };
279
280 protected:
281 jsonifier::vector<int8_t> sentinelVector{};
282 int8_t maxLookAheadDistance{ minLookups };
283 size_type capacityVal{};
284 size_type sizeVal{};
285 value_type* data{};
286
287 template<typename mapped_type_new> inline iterator emplaceInternal(mapped_type_new&& value) {
288 if (full() || capacityVal == 0) {
289 resize(capacityVal + 1);
290 }
291 auto currentIndex = getKeyAccessor()(value) & (capacityVal - 1);
292 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
293 if (sentinelVector[currentIndex] == 0) {
294 new (std::addressof(data[currentIndex])) value_type{ std::forward<mapped_type_new>(value) };
295 sentinelVector[currentIndex] = 1;
296 sizeVal++;
297 return { this, currentIndex };
298 } else if (sentinelVector[currentIndex] > 0 && object_compare()(getKeyAccessor()(data[currentIndex]), getKeyAccessor()(value))) {
299 if constexpr (!std::is_void_v<mapped_type_new>) {
300 data[currentIndex] = std::forward<mapped_type_new>(value);
301 }
302 return { this, currentIndex };
303 }
304 }
305 resize(capacityVal + 1);
306 return emplaceInternal(std::forward<mapped_type_new>(value));
307 }
308
309 inline allocator& getAlloc() {
310 return *this;
311 }
312
313 inline const hash_policy& getHashPolicy() const {
314 return *this;
315 }
316
317 inline const key_accessor& getKeyAccessor() const {
318 return *this;
319 }
320
321 inline void resize(size_type capacityNew) {
322 auto newSize = getHashPolicy().nextPowerOfTwo(capacityNew);
323 if (newSize > capacityVal) {
324 jsonifier::vector<int8_t> oldSentinelVector = std::move(sentinelVector);
325 auto oldMaxLookAheadDistance = maxLookAheadDistance;
326 auto oldCapacity = capacityVal;
327 auto oldSize = sizeVal;
328 auto oldPtr = data;
329 maxLookAheadDistance = getHashPolicy().computeMaxLookAheadDistance(newSize);
330 sizeVal = 0;
331 data = getAlloc().allocate(newSize + 1 + maxLookAheadDistance);
332 sentinelVector.resize(newSize + 1 + maxLookAheadDistance);
333 sentinelVector[newSize + maxLookAheadDistance] = -1;
334 capacityVal = newSize;
335 for (size_type x = 0, y = 0; y < oldSize; ++x) {
336 if (oldSentinelVector[x] > 0) {
337 ++y;
338 emplaceInternal(std::move(oldPtr[x]));
339 }
340 }
341 if (oldPtr && oldCapacity) {
342 getAlloc().deallocate(oldPtr, oldCapacity + 1 + oldMaxLookAheadDistance);
343 }
344 }
345 }
346
347 inline void reset() {
348 if (data && sizeVal > 0) {
349 for (uint64_t x = 0; x < sentinelVector.size(); ++x) {
350 if (sentinelVector[x] > 0) {
351 getAlloc().destroy(data + x);
352 }
353 }
354 getAlloc().deallocate(data, capacityVal + 1 + maxLookAheadDistance);
355 sentinelVector.clear();
356 sizeVal = 0;
357 capacityVal = 0;
358 data = nullptr;
359 }
360 }
361 };
362}