OLD | NEW |
1 /* | 1 /* |
2 * This file is part of Adblock Plus <https://adblockplus.org/>, | 2 * This file is part of Adblock Plus <https://adblockplus.org/>, |
3 * Copyright (C) 2006-present eyeo GmbH | 3 * Copyright (C) 2006-present eyeo GmbH |
4 * | 4 * |
5 * Adblock Plus is free software: you can redistribute it and/or modify | 5 * Adblock Plus is free software: you can redistribute it and/or modify |
6 * it under the terms of the GNU General Public License version 3 as | 6 * it under the terms of the GNU General Public License version 3 as |
7 * published by the Free Software Foundation. | 7 * published by the Free Software Foundation. |
8 * | 8 * |
9 * Adblock Plus is distributed in the hope that it will be useful, | 9 * Adblock Plus is distributed in the hope that it will be useful, |
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of | 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of |
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
12 * GNU General Public License for more details. | 12 * GNU General Public License for more details. |
13 * | 13 * |
14 * You should have received a copy of the GNU General Public License | 14 * You should have received a copy of the GNU General Public License |
15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. | 15 * along with Adblock Plus. If not, see <http://www.gnu.org/licenses/>. |
16 */ | 16 */ |
17 | 17 |
18 #pragma once | 18 #pragma once |
19 | 19 |
20 #include <cstddef> | |
21 #include <cmath> | 20 #include <cmath> |
22 #include <initializer_list> | 21 #include <initializer_list> |
23 #include <memory> | 22 #include <memory> |
24 | 23 |
25 #include "String.h" | |
26 #include "debug.h" | 24 #include "debug.h" |
27 | 25 |
28 template<typename T> | 26 template<typename Entry> |
29 class StringMap; | 27 class Map; |
30 | 28 |
31 namespace StringMap_internal | 29 namespace Map_internal |
32 { | 30 { |
33 template<typename Entry> | 31 template<typename Entry> |
34 struct HashContainerIterator | 32 struct HashContainerIterator |
35 { | 33 { |
36 typedef Entry entry_type; | 34 typedef Entry entry_type; |
37 typedef HashContainerIterator<Entry> iterator; | 35 typedef HashContainerIterator<Entry> iterator; |
38 | 36 |
39 const entry_type* mPos; | 37 const entry_type* mPos; |
40 const entry_type* mEnd; | 38 const entry_type* mEnd; |
41 | 39 |
42 explicit HashContainerIterator(const entry_type* start, const entry_type* en
d) | 40 explicit HashContainerIterator(const entry_type* start, const entry_type* en
d) |
43 : mPos(start), mEnd(end) | 41 : mPos(start), mEnd(end) |
44 { | 42 { |
45 if (mPos != mEnd && mPos->first.is_invalid()) | 43 if (mPos != mEnd && mPos->is_invalid()) |
46 ++(*this); | 44 ++(*this); |
47 } | 45 } |
48 | 46 |
49 const entry_type& operator*() const | 47 const entry_type& operator*() const |
50 { | 48 { |
51 return *mPos; | 49 return *mPos; |
52 } | 50 } |
53 | 51 |
54 const entry_type* operator->() const | 52 const entry_type* operator->() const |
55 { | 53 { |
56 return mPos; | 54 return mPos; |
57 } | 55 } |
58 | 56 |
59 iterator& operator++() | 57 iterator& operator++() |
60 { | 58 { |
61 do { | 59 do { |
62 ++mPos; | 60 ++mPos; |
63 } while(mPos != mEnd && mPos->first.is_invalid()); | 61 } while(mPos != mEnd && mPos->is_invalid()); |
64 return *this; | 62 return *this; |
65 } | 63 } |
66 | 64 |
67 bool operator==(const iterator& it) const | 65 bool operator==(const iterator& it) const |
68 { | 66 { |
69 return mPos == it.mPos; | 67 return mPos == it.mPos; |
70 } | 68 } |
71 | 69 |
72 bool operator!=(const iterator& it) const | 70 bool operator!=(const iterator& it) const |
73 { | 71 { |
(...skipping 13 matching lines...) Expand all Loading... |
87 { | 85 { |
88 } | 86 } |
89 | 87 |
90 const entry_type* operator->() const | 88 const entry_type* operator->() const |
91 { | 89 { |
92 return mEntry; | 90 return mEntry; |
93 } | 91 } |
94 | 92 |
95 operator bool() const | 93 operator bool() const |
96 { | 94 { |
97 return !mEntry->first.is_invalid(); | 95 return !mEntry->is_invalid(); |
98 } | 96 } |
99 }; | 97 }; |
100 | 98 |
101 template<typename Entry> | 99 template<typename Entry> |
102 class HashContainer | 100 class HashContainer |
103 { | 101 { |
104 public: | 102 public: |
105 typedef Entry entry_type; | 103 typedef Entry entry_type; |
106 typedef size_t size_type; | 104 typedef typename Entry::key_type key_type; |
| 105 typedef typename entry_type::size_type size_type; |
107 typedef HashContainerIterator<Entry> const_iterator; | 106 typedef HashContainerIterator<Entry> const_iterator; |
108 typedef HashContainerReference<const Entry> const_reference; | 107 typedef HashContainerReference<const Entry> const_reference; |
109 | 108 |
110 private: | 109 private: |
111 explicit HashContainer(const HashContainer& other); | 110 explicit HashContainer(const HashContainer& other); |
112 void operator=(const HashContainer& other); | 111 void operator=(const HashContainer& other); |
113 | 112 |
114 protected: | 113 protected: |
115 static constexpr size_type MIN_BUCKETS = 1; | 114 static constexpr size_type MIN_BUCKETS = 1; |
116 static constexpr double LOAD_FACTOR = 0.8; | 115 static constexpr double LOAD_FACTOR = 0.8; |
117 std::unique_ptr<entry_type[]> mBuckets; | 116 std::unique_ptr<entry_type[]> mBuckets; |
118 size_type mBucketCount; | 117 size_type mBucketCount; |
119 size_type mEntryCount; | 118 size_type mEntryCount; |
120 | 119 |
121 #if defined(DEBUG) | 120 #if defined(DEBUG) |
122 size_type mInsertCounter; | 121 size_type mInsertCounter; |
123 #endif | 122 #endif |
124 | 123 |
125 explicit HashContainer(size_type expectedEntries = 0) | 124 entry_type* find_bucket(const key_type& key) const |
126 : mEntryCount(0) | |
127 { | 125 { |
128 expectedEntries = ceil(expectedEntries / LOAD_FACTOR); | 126 size_type h = entry_type::hash(key); |
129 mBucketCount = MIN_BUCKETS; | |
130 while (mBucketCount < expectedEntries) | |
131 mBucketCount <<= 1; | |
132 | |
133 mBuckets.reset(new entry_type[mBucketCount]); | |
134 // Working around https://github.com/waywardmonkeys/emscripten-trace-colle
ctor/issues/2 here | |
135 annotate_address(reinterpret_cast<size_type*>(mBuckets.get()) - 1, "Hash t
able buffer"); | |
136 } | |
137 | |
138 static size_type hash(const String& str) | |
139 { | |
140 // FNV-1a hash function | |
141 size_type result = 2166136261; | |
142 for (String::size_type i = 0; i < str.length(); i++) | |
143 result = (result ^ str[i]) * 16777619; | |
144 return result; | |
145 } | |
146 | |
147 entry_type* find_bucket(const String& key) const | |
148 { | |
149 size_type h = hash(key); | |
150 | 127 |
151 // This does quadratic probing, effectively the following formula is used: | 128 // This does quadratic probing, effectively the following formula is used: |
152 // pos = (hash + 1/2 i + 1/2 i ^ 2) mod bucketCount | 129 // pos = (hash + 1/2 i + 1/2 i ^ 2) mod bucketCount |
153 for (size_type i = 0; ; ++i) | 130 for (size_type i = 0; ; ++i) |
154 { | 131 { |
155 // mBucketCount is 2^n so (h & mBucketCount - 1) is equivalent to | 132 // mBucketCount is 2^n so (h & mBucketCount - 1) is equivalent to |
156 // h % mBucketCount but significantly faster. | 133 // h % mBucketCount but significantly faster. |
157 entry_type* entry = &mBuckets[h & (mBucketCount - 1)]; | 134 entry_type* entry = &mBuckets[h & (mBucketCount - 1)]; |
158 if (entry->first.is_invalid() || entry->first.equals(key)) | 135 if (entry->is_invalid() || entry->equals(key)) |
159 return entry; | 136 return entry; |
160 h += i; | 137 h += i; |
161 } | 138 } |
162 } | 139 } |
163 | 140 |
164 void resize(size_type bucketCount) | 141 void resize(size_type bucketCount) |
165 { | 142 { |
166 std::unique_ptr<entry_type[]> oldBuckets(std::move(mBuckets)); | 143 std::unique_ptr<entry_type[]> oldBuckets(std::move(mBuckets)); |
167 size_type oldCount = mBucketCount; | 144 size_type oldCount = mBucketCount; |
168 | 145 |
169 mEntryCount = 0; | 146 mEntryCount = 0; |
170 mBucketCount = bucketCount; | 147 mBucketCount = bucketCount; |
171 mBuckets.reset(new entry_type[mBucketCount]); | 148 mBuckets.reset(new entry_type[mBucketCount]); |
172 // Working around https://github.com/waywardmonkeys/emscripten-trace-colle
ctor/issues/2 here | 149 // Working around https://github.com/waywardmonkeys/emscripten-trace-colle
ctor/issues/2 here |
173 annotate_address(reinterpret_cast<size_type*>(mBuckets.get()) - 1, "Hash t
able buffer"); | 150 annotate_address(reinterpret_cast<size_type*>(mBuckets.get()) - 1, "Hash t
able buffer"); |
174 | 151 |
175 // Copy old entries into the new buffer | 152 // Copy old entries into the new buffer |
176 for (size_type i = 0; i < oldCount; i++) | 153 for (size_type i = 0; i < oldCount; i++) |
177 { | 154 { |
178 entry_type& entry = oldBuckets[i]; | 155 entry_type& entry = oldBuckets[i]; |
179 if (!entry.first.is_invalid() && !entry.first.is_deleted()) | 156 if (!entry.is_invalid() && !entry.is_deleted()) |
180 { | 157 { |
181 *find_bucket(entry.first) = entry; | 158 *find_bucket(entry.first) = entry; |
182 mEntryCount++; | 159 mEntryCount++; |
183 } | 160 } |
184 } | 161 } |
185 } | 162 } |
186 | 163 |
187 entry_type* assign(entry_type* existing, const entry_type& entry) | 164 entry_type* assign(entry_type* existing, const entry_type& entry) |
188 { | 165 { |
189 if (existing->first.is_invalid()) | 166 if (existing->is_invalid()) |
190 { | 167 { |
191 if (mEntryCount + 1 >= mBucketCount * LOAD_FACTOR) | 168 if (mEntryCount + 1 >= mBucketCount * LOAD_FACTOR) |
192 { | 169 { |
193 resize(mBucketCount << 1); | 170 resize(mBucketCount << 1); |
194 existing = find_bucket(entry.first); | 171 existing = find_bucket(entry.first); |
195 } | 172 } |
196 mEntryCount++; | 173 mEntryCount++; |
197 #if defined(DEBUG) | 174 #if defined(DEBUG) |
198 mInsertCounter++; | 175 mInsertCounter++; |
199 #endif | 176 #endif |
200 } | 177 } |
201 *existing = entry; | 178 *existing = entry; |
202 return existing; | 179 return existing; |
203 } | 180 } |
204 | 181 |
205 public: | 182 public: |
| 183 explicit HashContainer(size_type expectedEntries = 0) |
| 184 : mEntryCount(0) |
| 185 { |
| 186 expectedEntries = ceil(expectedEntries / LOAD_FACTOR); |
| 187 mBucketCount = MIN_BUCKETS; |
| 188 while (mBucketCount < expectedEntries) |
| 189 mBucketCount <<= 1; |
| 190 |
| 191 mBuckets.reset(new entry_type[mBucketCount]); |
| 192 // Working around https://github.com/waywardmonkeys/emscripten-trace-colle
ctor/issues/2 here |
| 193 annotate_address(reinterpret_cast<size_type*>(mBuckets.get()) - 1, "Hash t
able buffer"); |
| 194 } |
| 195 |
206 void insert(const entry_type& entry) | 196 void insert(const entry_type& entry) |
207 { | 197 { |
208 assign(find_bucket(entry.first), entry); | 198 assign(find_bucket(entry.first), entry); |
209 } | 199 } |
210 | 200 |
211 bool erase(const String& key) | 201 bool erase(const key_type& key) |
212 { | 202 { |
213 entry_type* entry = find_bucket(key); | 203 entry_type* entry = find_bucket(key); |
214 if (entry->first.is_invalid()) | 204 if (entry->is_invalid()) |
215 return false; | 205 return false; |
216 | 206 |
217 entry->first.erase(); | 207 entry->erase(); |
218 return true; | 208 return true; |
219 } | 209 } |
220 | 210 |
221 const_reference find(const String& key) const | 211 const_reference find(const key_type& key) const |
222 { | 212 { |
223 return const_reference(find_bucket(key)); | 213 return const_reference(find_bucket(key)); |
224 } | 214 } |
225 | 215 |
226 const_iterator begin() const | 216 const_iterator begin() const |
227 { | 217 { |
228 return const_iterator(&mBuckets[0], &mBuckets[mBucketCount]); | 218 return const_iterator(&mBuckets[0], &mBuckets[mBucketCount]); |
229 } | 219 } |
230 | 220 |
231 const_iterator end() const | 221 const_iterator end() const |
232 { | 222 { |
233 return const_iterator(&mBuckets[mBucketCount], &mBuckets[mBucketCount]); | 223 return const_iterator(&mBuckets[mBucketCount], &mBuckets[mBucketCount]); |
234 } | 224 } |
235 | 225 |
236 size_type size() const | 226 size_type size() const |
237 { | 227 { |
238 return mEntryCount; | 228 return mEntryCount; |
239 } | 229 } |
240 }; | 230 }; |
241 | 231 |
242 struct StringSetEntry | 232 template<typename Entry> |
| 233 struct MapReference : public HashContainerReference<Entry> |
243 { | 234 { |
244 StringSetEntry() {} | 235 typedef HashContainerReference<Entry> super; |
245 StringSetEntry(const String& key) | |
246 : first(key) | |
247 { | |
248 } | |
249 | |
250 DependentString first; | |
251 }; | |
252 | |
253 template<typename T> | |
254 struct StringMapEntry | |
255 { | |
256 StringMapEntry() {} | |
257 StringMapEntry(const String& key) | |
258 : first(key), second() | |
259 { | |
260 } | |
261 StringMapEntry(const String& key, T value) | |
262 : first(key), second(value) | |
263 { | |
264 } | |
265 | |
266 DependentString first; | |
267 T second; | |
268 }; | |
269 | |
270 template<typename T> | |
271 struct StringMapEntryReference : public HashContainerReference<StringMapEntry<
T>> | |
272 { | |
273 typedef HashContainerReference<StringMapEntry<T>> super; | |
274 typedef typename super::entry_type entry_type; | 236 typedef typename super::entry_type entry_type; |
275 typedef StringMap<T> map_type; | 237 typedef typename entry_type::key_type key_type; |
| 238 typedef typename entry_type::value_type value_type; |
| 239 typedef Map<entry_type> map_type; |
276 | 240 |
277 map_type* mMap; | 241 map_type* mMap; |
278 | 242 |
279 #if defined(DEBUG) | 243 #if defined(DEBUG) |
280 typename map_type::size_type mInsertCounter; | 244 typename map_type::size_type mInsertCounter; |
281 typename map_type::size_type mHash; | 245 typename map_type::size_type mHash; |
282 #endif | 246 #endif |
283 | 247 |
284 StringMapEntryReference(map_type* map, const String& key, entry_type* entry) | 248 MapReference(map_type* map, const key_type& key, entry_type* entry) |
285 : super(entry), mMap(map) | 249 : super(entry), mMap(map) |
286 { | 250 { |
287 #if defined(DEBUG) | 251 #if defined(DEBUG) |
288 mInsertCounter = mMap->mInsertCounter; | 252 mInsertCounter = mMap->mInsertCounter; |
289 mHash = mMap->hash(key); | 253 mHash = entry_type::hash(key); |
290 #endif | 254 #endif |
291 } | 255 } |
292 | 256 |
293 void assign(const String& key, const T& value) | 257 void assign(const key_type& key, const value_type& value) |
294 { | 258 { |
295 #if defined(DEBUG) | 259 #if defined(DEBUG) |
296 assert2(mInsertCounter == mMap->mInsertCounter, | 260 assert2(mInsertCounter == mMap->mInsertCounter, |
297 u"There should be no insert operations performed between map.find() an
d assign()"_str); | 261 u"There should be no insert operations performed between map.find() an
d assign()"_str); |
298 assert2(mHash == mMap->hash(key), | 262 assert2(mHash == entry_type::hash(key), |
299 u"The keys used in map.find() and assign() should be identical"_str); | 263 u"The keys used in map.find() and assign() should be identical"_str); |
300 #endif | 264 #endif |
301 | 265 |
302 mMap->assign(this->mEntry, entry_type(key, value)); | 266 mMap->assign(this->mEntry, entry_type(key, value)); |
303 } | 267 } |
304 }; | 268 }; |
305 } | 269 } |
306 | 270 |
307 class StringSet | 271 template<typename Entry> |
308 : public StringMap_internal::HashContainer<StringMap_internal::StringSetEntry> | 272 class Set : public Map_internal::HashContainer<Entry> |
309 { | 273 { |
| 274 public: |
| 275 typedef Map_internal::HashContainer<Entry> super; |
| 276 |
| 277 using super::super; |
310 }; | 278 }; |
311 | 279 |
312 template<typename T> | 280 template<typename Entry> |
313 class StringMap | 281 class Map : public Map_internal::HashContainer<Entry> |
314 : public StringMap_internal::HashContainer<StringMap_internal::StringMapEntry<
T>> | |
315 { | 282 { |
316 public: | 283 public: |
317 typedef StringMap_internal::HashContainer<StringMap_internal::StringMapEntry<T
>> super; | 284 typedef Map_internal::HashContainer<Entry> super; |
318 typedef typename super::size_type size_type; | 285 typedef typename super::size_type size_type; |
319 typedef typename super::entry_type entry_type; | 286 typedef typename super::entry_type entry_type; |
| 287 typedef typename super::key_type key_type; |
| 288 typedef typename entry_type::value_type value_type; |
320 typedef typename super::const_reference const_reference; | 289 typedef typename super::const_reference const_reference; |
321 typedef StringMap_internal::StringMapEntryReference<T> reference; | 290 typedef Map_internal::MapReference<entry_type> reference; |
322 friend struct StringMap_internal::StringMapEntryReference<T>; | 291 friend struct Map_internal::MapReference<entry_type>; |
323 | 292 |
324 explicit StringMap(size_type expectedEntries = 0) | 293 using super::super; |
325 : super(expectedEntries) | |
326 { | |
327 } | |
328 | 294 |
329 StringMap(std::initializer_list<entry_type> list) | 295 Map(std::initializer_list<entry_type> list) |
330 : super(list.size()) | 296 : super(list.size()) |
331 { | 297 { |
332 for (const auto& item : list) | 298 for (const auto& item : list) |
333 super::insert(item); | 299 super::insert(item); |
334 } | 300 } |
335 | 301 |
336 ~StringMap() | 302 value_type& operator[](const key_type& key) |
337 { | |
338 } | |
339 | |
340 T& operator[](const String& key) | |
341 { | 303 { |
342 entry_type* entry = super::find_bucket(key); | 304 entry_type* entry = super::find_bucket(key); |
343 if (entry->first.is_invalid()) | 305 if (entry->is_invalid()) |
344 entry = super::assign(entry, key); | 306 entry = super::assign(entry, key); |
345 return entry->second; | 307 return entry->second; |
346 } | 308 } |
347 | 309 |
348 const_reference find(const String& key) const | 310 const_reference find(const key_type& key) const |
349 { | 311 { |
350 return super::find(key); | 312 return super::find(key); |
351 } | 313 } |
352 | 314 |
353 reference find(const String& key) | 315 reference find(const key_type& key) |
354 { | 316 { |
355 return reference(this, key, super::find_bucket(key)); | 317 return reference(this, key, super::find_bucket(key)); |
356 } | 318 } |
357 }; | 319 }; |
OLD | NEW |