DiscordCoreAPI
A Discord bot library written in C++, with custom asynchronous coroutines.
Loading...
Searching...
No Matches
UnorderedMap.hpp
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/// unordered_map.hpp - Header file for the unordered_map class.
27/// May 12, 2021
28/// https://discordcoreapi.com
29/// \file unordered_map.hpp
30#pragma once
31
34
35#include <memory_resource>
36#include <shared_mutex>
37#include <exception>
38#include <optional>
39#include <utility>
40#include <vector>
41#include <mutex>
42
43namespace discord_core_api {
44
45 template<typename key_type, typename value_type> class unordered_map;
46
47 template<typename map_iterator, typename key_type, typename value_type>
48 concept map_container_iterator_t = std::same_as<typename unordered_map<key_type, value_type>::iterator, std::decay_t<map_iterator>>;
49
50 template<typename key_type_new, typename value_type_new> class unordered_map : protected hash_policy<unordered_map<key_type_new, value_type_new>>,
51 protected jsonifier_internal::alloc_wrapper<std::pair<key_type_new, value_type_new>>,
52 protected object_compare {
53 public:
54 using key_type = key_type_new;
55 using value_type = std::pair<key_type_new, value_type_new>;
56 using allocator_type = jsonifier_internal::alloc_wrapper<value_type>;
57 using allocator_traits = std::allocator_traits<allocator_type>;
58 using size_type = uint64_t;
59 using difference_type = int64_t;
60 using pointer = typename allocator_traits::pointer;
61 using const_pointer = typename allocator_traits::const_pointer;
62 using mapped_type = value_type_new;
63 using reference = value_type&;
64 using const_reference = const value_type&;
65 using iterator = hash_iterator<unordered_map<key_type, mapped_type>>;
66 using const_iterator = hash_iterator<const unordered_map<key_type, mapped_type>>;
67 using object_compare = object_compare;
68 using hash_policy_new = hash_policy<unordered_map<key_type, mapped_type>>;
69
70 friend hash_policy_new;
71 friend iterator;
72 friend const_iterator;
73
74 DCA_INLINE unordered_map(){};
75
76 DCA_INLINE unordered_map& operator=(unordered_map&& other) noexcept {
77 if (this != &other) {
78 reset();
79 swap(other);
80 }
81 return *this;
82 }
83
84 DCA_INLINE unordered_map(unordered_map&& other) noexcept {
85 *this = std::move(other);
86 }
87
88 DCA_INLINE unordered_map& operator=(const unordered_map& other) {
89 if (this != &other) {
90 reset();
91
92 reserve(other.capacity());
93 for (const auto& [key, value]: other) {
94 emplace(key, value);
95 }
96 }
97 return *this;
98 }
99
100 DCA_INLINE unordered_map(const unordered_map& other) {
101 *this = other;
102 }
103
104 DCA_INLINE unordered_map(std::initializer_list<value_type> list) {
105 reserve(list.size());
106 for (auto& value: list) {
107 emplace(std::move(value.first), std::move(value.second));
108 }
109 };
110
111 template<typename... Args> iterator emplace(Args&&... value) {
112 return emplaceInternal(std::forward<Args>(value)...);
113 }
114
115 template<typename key_type_newer> DCA_INLINE const_iterator find(key_type_newer&& key) const {
116 if (sizeVal > 0) {
117 auto currentIndex = hash_policy_new::indexForHash(key);
118 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
119 if (sentinelVector[currentIndex] > 0 && object_compare()(data[currentIndex].first, key)) {
120 return { this, currentIndex };
121 }
122 }
123 }
124 return end();
125 }
126
127 template<typename key_type_newer> DCA_INLINE iterator find(key_type_newer&& key) {
128 if (sizeVal > 0) {
129 auto currentIndex = hash_policy_new::indexForHash(key);
130 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
131 if (sentinelVector[currentIndex] > 0 && object_compare()(data[currentIndex].first, key)) {
132 return { this, currentIndex };
133 }
134 }
135 }
136 return end();
137 }
138
139 template<typename key_type_newer> DCA_INLINE const mapped_type& operator[](key_type_newer&& key) const {
140 return emplaceInternal(key)->second;
141 }
142
143 template<typename key_type_newer> DCA_INLINE mapped_type& operator[](key_type_newer&& key) {
144 return emplaceInternal(key)->second;
145 }
146
147 template<typename key_type_newer> DCA_INLINE const mapped_type& at(key_type_newer&& key) const {
148 auto iter = find(std::forward<key_type_newer>(key));
149 if (iter == end()) {
150 throw std::runtime_error{ "Sorry, but an object by that key doesn't exist in this map." };
151 }
152 return iter->second;
153 }
154
155 template<typename key_type_newer> DCA_INLINE mapped_type& at(key_type_newer&& key) {
156 auto iter = find(std::forward<key_type_newer>(key));
157 if (iter == end()) {
158 throw std::runtime_error{ "Sorry, but an object by that key doesn't exist in this map." };
159 }
160 return iter->second;
161 }
162
163 template<typename key_type_newer> DCA_INLINE bool contains(key_type_newer&& key) const {
164 if (sizeVal > 0) {
165 auto currentIndex = hash_policy_new::indexForHash(key);
166 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
167 if (sentinelVector[currentIndex] > 0 && object_compare()(data[currentIndex].first, key)) {
168 return true;
169 }
170 }
171 }
172 return false;
173 }
174
175 template<map_container_iterator_t<key_type, mapped_type> map_iterator> DCA_INLINE iterator erase(map_iterator&& iter) {
176 if (sizeVal > 0) {
177 auto currentIndex = static_cast<size_type>(iter.getRawPtr() - data);
178 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
179 if (sentinelVector[currentIndex] > 0 && object_compare()(data[currentIndex], iter.operator*())) {
180 allocator_traits::destroy(*this, data + currentIndex);
181 sentinelVector[currentIndex] = 0;
182 --sizeVal;
183 return { this, ++currentIndex };
184 }
185 }
186 }
187 return end();
188 }
189
190 template<typename key_type_newer> DCA_INLINE iterator erase(key_type_newer&& key) {
191 if (sizeVal > 0) {
192 auto currentIndex = hash_policy_new::indexForHash(key);
193 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
194 if (sentinelVector[currentIndex] > 0 && object_compare()(data[currentIndex].first, key)) {
195 allocator_traits::destroy(*this, data + currentIndex);
196 sentinelVector[currentIndex] = 0;
197 --sizeVal;
198 return { this, ++currentIndex };
199 }
200 }
201 }
202 return end();
203 }
204
205 DCA_INLINE const_iterator begin() const {
206 for (size_type x{ 0 }; x < capacityVal; ++x) {
207 if (sentinelVector.at(x) > 0) {
208 return { this, x };
209 }
210 }
211 return end();
212 }
213
214 DCA_INLINE const_iterator end() const {
215 return {};
216 }
217
218 DCA_INLINE iterator begin() {
219 for (size_type x{ 0 }; x < capacityVal; ++x) {
220 if (sentinelVector.at(x) > 0) {
221 return { this, x };
222 }
223 }
224 return end();
225 }
226
227 DCA_INLINE iterator end() {
228 return {};
229 }
230
231 DCA_INLINE bool full() const {
232 return static_cast<float>(sizeVal) >= static_cast<float>(capacityVal) * 0.90f;
233 }
234
235 DCA_INLINE size_type size() const {
236 return sizeVal;
237 }
238
239 DCA_INLINE bool empty() const {
240 return sizeVal == 0;
241 }
242
243 DCA_INLINE void reserve(size_type sizeNew) {
244 resize(sizeNew);
245 }
246
247 DCA_INLINE void swap(unordered_map& other) {
248 std::swap(maxLookAheadDistance, other.maxLookAheadDistance);
249 std::swap(sentinelVector, other.sentinelVector);
250 std::swap(capacityVal, other.capacityVal);
251 std::swap(sizeVal, other.sizeVal);
252 std::swap(data, other.data);
253 }
254
255 DCA_INLINE size_type capacity() const {
256 return capacityVal;
257 }
258
259 DCA_INLINE bool operator==(const unordered_map& other) const {
260 if (capacityVal != other.capacityVal || sizeVal != other.sizeVal || data != other.data) {
261 return false;
262 }
263 for (auto iter01{ begin() }, iter02{ other.begin() }; iter01 != end(); ++iter01, ++iter02) {
264 if (!object_compare()(iter01.operator*().second, iter02.operator*().second)) {
265 return false;
266 }
267 }
268 return true;
269 }
270
271 DCA_INLINE void clear() {
272 for (size_type x = 0; x < sentinelVector.size(); ++x) {
273 if (sentinelVector.at(x) > 0) {
274 allocator_traits::destroy(*this, data + x);
275 sentinelVector.at(x) = 0;
276 }
277 }
278 sizeVal = 0;
279 }
280
281 DCA_INLINE ~unordered_map() {
282 reset();
283 };
284
285 protected:
286 jsonifier::vector<int8_t> sentinelVector{};
287 int8_t maxLookAheadDistance{ 0 };
288 size_type capacityVal{};
289 size_type sizeVal{};
290 value_type* data{};
291
292 template<typename key_type_newer, typename... mapped_type_new> DCA_INLINE iterator emplaceInternal(key_type_newer&& key, mapped_type_new&&... value) {
293 if (full() || capacityVal == 0) {
294 resize(capacityVal + 1);
295 }
296 auto currentIndex = hash_policy_new::indexForHash(key);
297 for (size_type x{}; x < static_cast<size_type>(maxLookAheadDistance); ++x, ++currentIndex) {
298 if (sentinelVector[currentIndex] == 0) {
299 if constexpr ((( !std::is_void_v<mapped_type_new> ) || ...)) {
300 new (std::addressof(data[currentIndex])) value_type{ std::make_pair(std::forward<key_type_newer>(key), std::forward<mapped_type_new>(value)...) };
301 } else {
302 new (std::addressof(data[currentIndex])) value_type{ std::make_pair(std::forward<key_type_newer>(key), mapped_type{}) };
303 }
304 sentinelVector[currentIndex] = 1;
305 sizeVal++;
306 return { this, currentIndex };
307 } else if (sentinelVector[currentIndex] > 0 && object_compare()(data[currentIndex].first, key)) {
308 if constexpr ((( !std::is_void_v<mapped_type_new> ) || ...)) {
309 data[currentIndex].second = mapped_type{ std::forward<mapped_type_new>(value)... };
310 }
311 return { this, currentIndex };
312 }
313 }
314 resize(capacityVal + 1);
315 return emplaceInternal(std::forward<key_type_newer>(key), std::forward<mapped_type_new>(value)...);
316 }
317
318 DCA_INLINE void resize(size_type capacityNew) {
319 auto newSize = hash_policy_new::nextPowerOfTwo(capacityNew);
320 if (newSize > capacityVal) {
321 jsonifier::vector<int8_t> oldSentinelVector = std::move(sentinelVector);
322 auto oldMaxLookAheadDistance = maxLookAheadDistance;
323 auto oldCapacity = capacityVal;
324 auto oldSize = sizeVal;
325 auto oldPtr = data;
326 maxLookAheadDistance = hash_policy_new::computeMaxLookAheadDistance(newSize);
327 sizeVal = 0;
328 data = allocator_traits::allocate(*this, newSize + 1 + maxLookAheadDistance);
329 sentinelVector.resize(newSize + 1 + maxLookAheadDistance);
330 sentinelVector[newSize + maxLookAheadDistance] = -1;
331 capacityVal = newSize;
332 for (size_type x = 0, y = 0; y < oldSize; ++x) {
333 if (oldSentinelVector.at(x) > 0) {
334 ++y;
335 emplaceInternal(std::move(oldPtr[x].first), std::move(oldPtr[x].second));
336 }
337 }
338 if (oldPtr && oldCapacity) {
339 allocator_traits::deallocate(*this, oldPtr, oldCapacity + 1 + oldMaxLookAheadDistance);
340 }
341 }
342 }
343
344 DCA_INLINE void reset() {
345 if (data && sizeVal > 0) {
346 for (uint64_t x = 0; x < sentinelVector.size(); ++x) {
347 if (sentinelVector.at(x) > 0) {
348 allocator_traits::destroy(*this, data + x);
349 sentinelVector.at(x) = 0;
350 }
351 }
352 allocator_traits::deallocate(*this, data, capacityVal + 1 + maxLookAheadDistance);
353 sentinelVector.clear();
354 sizeVal = 0;
355 capacityVal = 0;
356 data = nullptr;
357 }
358 }
359 };
360
361
362}
The main namespace for the forward-facing interfaces.