| 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 |