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