1 /* 2 * Copyright 2013 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef SkTMultiMap_DEFINED 9 #define SkTMultiMap_DEFINED 10 11 #include "src/core/SkTDynamicHash.h" 12 13 /** A set that contains pointers to instances of T. Instances can be looked up with key Key. 14 * Multiple (possibly same) values can have the same key. 15 */ 16 template <typename T, 17 typename Key, 18 typename HashTraits=T> 19 class SkTMultiMap { 20 struct ValueList { ValueListValueList21 explicit ValueList(T* value) : fValue(value), fNext(nullptr) {} 22 GetKeyValueList23 static const Key& GetKey(const ValueList& e) { return HashTraits::GetKey(*e.fValue); } HashValueList24 static uint32_t Hash(const Key& key) { return HashTraits::Hash(key); } 25 T* fValue; 26 ValueList* fNext; 27 }; 28 public: SkTMultiMap()29 SkTMultiMap() : fCount(0) {} 30 ~SkTMultiMap()31 ~SkTMultiMap() { 32 this->reset(); 33 } 34 reset()35 void reset() { 36 fHash.foreach([&](ValueList* vl) { 37 ValueList* next; 38 for (ValueList* it = vl; it; it = next) { 39 HashTraits::OnFree(it->fValue); 40 next = it->fNext; 41 delete it; 42 } 43 }); 44 fHash.reset(); 45 fCount = 0; 46 } 47 insert(const Key & key,T * value)48 void insert(const Key& key, T* value) { 49 ValueList* list = fHash.find(key); 50 if (list) { 51 // The new ValueList entry is inserted as the second element in the 52 // linked list, and it will contain the value of the first element. 53 ValueList* newEntry = new ValueList(list->fValue); 54 newEntry->fNext = list->fNext; 55 // The existing first ValueList entry is updated to contain the 56 // inserted value. 57 list->fNext = newEntry; 58 list->fValue = value; 59 } else { 60 fHash.add(new ValueList(value)); 61 } 62 63 ++fCount; 64 } 65 remove(const Key & key,const T * value)66 void remove(const Key& key, const T* value) { 67 ValueList* list = fHash.find(key); 68 // Temporarily making this safe for remove entries not in the map because of 69 // crbug.com/877915. 70 #if 0 71 // Since we expect the caller to be fully aware of what is stored, just 72 // assert that the caller removes an existing value. 73 SkASSERT(list); 74 ValueList* prev = nullptr; 75 while (list->fValue != value) { 76 prev = list; 77 list = list->fNext; 78 } 79 this->internalRemove(prev, list, key); 80 #else 81 ValueList* prev = nullptr; 82 while (list && list->fValue != value) { 83 prev = list; 84 list = list->fNext; 85 } 86 // Crash in Debug since it'd be great to detect a repro of 877915. 87 SkASSERT(list); 88 if (list) { 89 this->internalRemove(prev, list, key); 90 } 91 #endif 92 } 93 find(const Key & key)94 T* find(const Key& key) const { 95 ValueList* list = fHash.find(key); 96 if (list) { 97 return list->fValue; 98 } 99 return nullptr; 100 } 101 102 template<class FindPredicate> find(const Key & key,const FindPredicate f)103 T* find(const Key& key, const FindPredicate f) { 104 ValueList* list = fHash.find(key); 105 while (list) { 106 if (f(list->fValue)){ 107 return list->fValue; 108 } 109 list = list->fNext; 110 } 111 return nullptr; 112 } 113 114 template<class FindPredicate> findAndRemove(const Key & key,const FindPredicate f)115 T* findAndRemove(const Key& key, const FindPredicate f) { 116 ValueList* list = fHash.find(key); 117 118 ValueList* prev = nullptr; 119 while (list) { 120 if (f(list->fValue)){ 121 T* value = list->fValue; 122 this->internalRemove(prev, list, key); 123 return value; 124 } 125 prev = list; 126 list = list->fNext; 127 } 128 return nullptr; 129 } 130 count()131 int count() const { return fCount; } 132 133 #ifdef SK_DEBUG 134 template <typename Fn> // f(T) or f(const T&) foreach(Fn && fn)135 void foreach(Fn&& fn) const { 136 fHash.foreach([&](const ValueList& vl) { 137 for (const ValueList* it = &vl; it; it = it->fNext) { 138 fn(*it->fValue); 139 } 140 }); 141 } 142 has(const T * value,const Key & key)143 bool has(const T* value, const Key& key) const { 144 for (ValueList* list = fHash.find(key); list; list = list->fNext) { 145 if (list->fValue == value) { 146 return true; 147 } 148 } 149 return false; 150 } 151 152 // This is not particularly fast and only used for validation, so debug only. countForKey(const Key & key)153 int countForKey(const Key& key) const { 154 int count = 0; 155 ValueList* list = fHash.find(key); 156 while (list) { 157 list = list->fNext; 158 ++count; 159 } 160 return count; 161 } 162 #endif 163 164 private: 165 SkTDynamicHash<ValueList, Key> fHash; 166 int fCount; 167 internalRemove(ValueList * prev,ValueList * elem,const Key & key)168 void internalRemove(ValueList* prev, ValueList* elem, const Key& key) { 169 if (elem->fNext) { 170 ValueList* next = elem->fNext; 171 elem->fValue = next->fValue; 172 elem->fNext = next->fNext; 173 delete next; 174 } else if (prev) { 175 prev->fNext = nullptr; 176 delete elem; 177 } else { 178 fHash.remove(key); 179 delete elem; 180 } 181 182 --fCount; 183 } 184 185 }; 186 187 #endif 188