RapidFuzz
Loading...
Searching...
No Matches
GrowingHashmap.hpp
1/* SPDX-License-Identifier: MIT */
2/* Copyright (c) 2022 Max Bachmann */
3
4#pragma once
5
6#include <algorithm>
7#include <utility>
8#include <array>
9#include <stddef.h>
10#include <stdint.h>
11
12namespace rapidfuzz {
13namespace detail {
14
15/* hashmap for integers which can only grow, but can't remove elements */
16template <typename T_Key, typename T_Entry>
17struct GrowingHashmap {
18 using key_type = T_Key;
19 using value_type = T_Entry;
20 using size_type = unsigned int;
21
22private:
23 static constexpr size_type min_size = 8;
24 struct MapElem {
25 key_type key;
26 value_type value = value_type();
27 };
28
29 int used;
30 int fill;
31 int mask;
32 MapElem* m_map;
33
34public:
35 GrowingHashmap() : used(0), fill(0), mask(-1), m_map(nullptr)
36 {}
37 ~GrowingHashmap()
38 {
39 delete[] m_map;
40 }
41
42 GrowingHashmap(const GrowingHashmap& other) : used(other.used), fill(other.fill), mask(other.mask)
43 {
44 int size = mask + 1;
45 m_map = new MapElem[size];
46 std::copy(other.m_map, other.m_map + size, m_map);
47 }
48
49 GrowingHashmap(GrowingHashmap&& other) noexcept : GrowingHashmap()
50 {
51 swap(*this, other);
52 }
53
54 GrowingHashmap& operator=(GrowingHashmap other)
55 {
56 swap(*this, other);
57 return *this;
58 }
59
60 friend void swap(GrowingHashmap& first, GrowingHashmap& second) noexcept
61 {
62 std::swap(first.used, second.used);
63 std::swap(first.fill, second.fill);
64 std::swap(first.mask, second.mask);
65 std::swap(first.m_map, second.m_map);
66 }
67
68 size_type size() const
69 {
70 return used;
71 }
72 size_type capacity() const
73 {
74 return mask + 1;
75 }
76 bool empty() const
77 {
78 return used == 0;
79 }
80
81 value_type get(key_type key) const noexcept
82 {
83 if (m_map == nullptr) return value_type();
84
85 return m_map[lookup(key)].value;
86 }
87
88 value_type& operator[](key_type key) noexcept
89 {
90 if (m_map == nullptr) allocate();
91
92 size_t i = lookup(key);
93
94 if (m_map[i].value == value_type()) {
95 /* resize when 2/3 full */
96 if (++fill * 3 >= (mask + 1) * 2) {
97 grow((used + 1) * 2);
98 i = lookup(key);
99 }
100
101 used++;
102 }
103
104 m_map[i].key = key;
105 return m_map[i].value;
106 }
107
108private:
109 void allocate()
110 {
111 mask = min_size - 1;
112 m_map = new MapElem[min_size];
113 }
114
119 size_t lookup(key_type key) const
120 {
121 size_t hash = static_cast<size_t>(key);
122 size_t i = hash & static_cast<size_t>(mask);
123
124 if (m_map[i].value == value_type() || m_map[i].key == key) return i;
125
126 size_t perturb = hash;
127 while (true) {
128 i = (i * 5 + perturb + 1) & static_cast<size_t>(mask);
129 if (m_map[i].value == value_type() || m_map[i].key == key) return i;
130
131 perturb >>= 5;
132 }
133 }
134
135 void grow(int minUsed)
136 {
137 int newSize = mask + 1;
138 while (newSize <= minUsed)
139 newSize <<= 1;
140
141 MapElem* oldMap = m_map;
142 m_map = new MapElem[static_cast<size_t>(newSize)];
143
144 fill = used;
145 mask = newSize - 1;
146
147 for (int i = 0; used > 0; i++)
148 if (oldMap[i].value != value_type()) {
149 size_t j = lookup(oldMap[i].key);
150
151 m_map[j].key = oldMap[i].key;
152 m_map[j].value = oldMap[i].value;
153 used--;
154 }
155
156 used = fill;
157 delete[] oldMap;
158 }
159};
160
161template <typename T_Key, typename T_Entry>
162struct HybridGrowingHashmap {
163 using key_type = T_Key;
164 using value_type = T_Entry;
165
166 HybridGrowingHashmap()
167 {
168 m_extendedAscii.fill(value_type());
169 }
170
171 value_type get(char key) const noexcept
172 {
174 return m_extendedAscii[static_cast<uint8_t>(key)];
175 }
176
177 template <typename CharT>
178 value_type get(CharT key) const noexcept
179 {
180 if (key >= 0 && key <= 255)
181 return m_extendedAscii[static_cast<uint8_t>(key)];
182 else
183 return m_map.get(static_cast<key_type>(key));
184 }
185
186 value_type& operator[](char key) noexcept
187 {
189 return m_extendedAscii[static_cast<uint8_t>(key)];
190 }
191
192 template <typename CharT>
193 value_type& operator[](CharT key)
194 {
195 if (key >= 0 && key <= 255)
196 return m_extendedAscii[static_cast<uint8_t>(key)];
197 else
198 return m_map[static_cast<key_type>(key)];
199 }
200
201private:
202 GrowingHashmap<key_type, value_type> m_map;
203 std::array<value_type, 256> m_extendedAscii;
204};
205
206} // namespace detail
207} // namespace rapidfuzz