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 "include/gpu/GrTypes.h" 12 #include "src/core/SkTDynamicHash.h" 13 14 /** A set that contains pointers to instances of T. Instances can be looked up with key Key. 15 * Multiple (possibly same) values can have the same key. 16 */ 17 template <typename T, 18 typename Key, 19 typename HashTraits=T> 20 class SkTMultiMap { 21 struct ValueList { ValueListValueList22 explicit ValueList(T* value) : fValue(value), fNext(nullptr) {} 23 GetKeyValueList24 static const Key& GetKey(const ValueList& e) { return HashTraits::GetKey(*e.fValue); } HashValueList25 static uint32_t Hash(const Key& key) { return HashTraits::Hash(key); } 26 T* fValue; 27 ValueList* fNext; 28 }; 29 public: SkTMultiMap()30 SkTMultiMap() : fCount(0) {} 31 ~SkTMultiMap()32 ~SkTMultiMap() { 33 typename SkTDynamicHash<ValueList, Key>::Iter iter(&fHash); 34 for ( ; !iter.done(); ++iter) { 35 ValueList* next; 36 for (ValueList* cur = &(*iter); cur; cur = next) { 37 HashTraits::OnFree(cur->fValue); 38 next = cur->fNext; 39 delete cur; 40 } 41 } 42 } 43 insert(const Key & key,T * value)44 void insert(const Key& key, T* value) { 45 ValueList* list = fHash.find(key); 46 if (list) { 47 // The new ValueList entry is inserted as the second element in the 48 // linked list, and it will contain the value of the first element. 49 ValueList* newEntry = new ValueList(list->fValue); 50 newEntry->fNext = list->fNext; 51 // The existing first ValueList entry is updated to contain the 52 // inserted value. 53 list->fNext = newEntry; 54 list->fValue = value; 55 } else { 56 fHash.add(new ValueList(value)); 57 } 58 59 ++fCount; 60 } 61 remove(const Key & key,const T * value)62 void remove(const Key& key, const T* value) { 63 ValueList* list = fHash.find(key); 64 // Temporarily making this safe for remove entries not in the map because of 65 // crbug.com/877915. 66 #if 0 67 // Since we expect the caller to be fully aware of what is stored, just 68 // assert that the caller removes an existing value. 69 SkASSERT(list); 70 ValueList* prev = nullptr; 71 while (list->fValue != value) { 72 prev = list; 73 list = list->fNext; 74 } 75 this->internalRemove(prev, list, key); 76 #else 77 ValueList* prev = nullptr; 78 while (list && list->fValue != value) { 79 prev = list; 80 list = list->fNext; 81 } 82 // Crash in Debug since it'd be great to detect a repro of 877915. 83 SkASSERT(list); 84 if (list) { 85 this->internalRemove(prev, list, key); 86 } 87 #endif 88 } 89 find(const Key & key)90 T* find(const Key& key) const { 91 ValueList* list = fHash.find(key); 92 if (list) { 93 return list->fValue; 94 } 95 return nullptr; 96 } 97 98 template<class FindPredicate> find(const Key & key,const FindPredicate f)99 T* find(const Key& key, const FindPredicate f) { 100 ValueList* list = fHash.find(key); 101 while (list) { 102 if (f(list->fValue)){ 103 return list->fValue; 104 } 105 list = list->fNext; 106 } 107 return nullptr; 108 } 109 110 template<class FindPredicate> findAndRemove(const Key & key,const FindPredicate f)111 T* findAndRemove(const Key& key, const FindPredicate f) { 112 ValueList* list = fHash.find(key); 113 114 ValueList* prev = nullptr; 115 while (list) { 116 if (f(list->fValue)){ 117 T* value = list->fValue; 118 this->internalRemove(prev, list, key); 119 return value; 120 } 121 prev = list; 122 list = list->fNext; 123 } 124 return nullptr; 125 } 126 count()127 int count() const { return fCount; } 128 129 #ifdef SK_DEBUG 130 class ConstIter { 131 public: ConstIter(const SkTMultiMap * mmap)132 explicit ConstIter(const SkTMultiMap* mmap) 133 : fIter(&(mmap->fHash)) 134 , fList(nullptr) { 135 if (!fIter.done()) { 136 fList = &(*fIter); 137 } 138 } 139 done()140 bool done() const { 141 return fIter.done(); 142 } 143 144 const T* operator*() { 145 SkASSERT(fList); 146 return fList->fValue; 147 } 148 149 void operator++() { 150 if (fList) { 151 fList = fList->fNext; 152 } 153 if (!fList) { 154 ++fIter; 155 if (!fIter.done()) { 156 fList = &(*fIter); 157 } 158 } 159 } 160 161 private: 162 typename SkTDynamicHash<ValueList, Key>::ConstIter fIter; 163 const ValueList* fList; 164 }; 165 has(const T * value,const Key & key)166 bool has(const T* value, const Key& key) const { 167 for (ValueList* list = fHash.find(key); list; list = list->fNext) { 168 if (list->fValue == value) { 169 return true; 170 } 171 } 172 return false; 173 } 174 175 // This is not particularly fast and only used for validation, so debug only. countForKey(const Key & key)176 int countForKey(const Key& key) const { 177 int count = 0; 178 ValueList* list = fHash.find(key); 179 while (list) { 180 list = list->fNext; 181 ++count; 182 } 183 return count; 184 } 185 #endif 186 187 private: 188 SkTDynamicHash<ValueList, Key> fHash; 189 int fCount; 190 internalRemove(ValueList * prev,ValueList * elem,const Key & key)191 void internalRemove(ValueList* prev, ValueList* elem, const Key& key) { 192 if (elem->fNext) { 193 ValueList* next = elem->fNext; 194 elem->fValue = next->fValue; 195 elem->fNext = next->fNext; 196 delete next; 197 } else if (prev) { 198 prev->fNext = nullptr; 199 delete elem; 200 } else { 201 fHash.remove(key); 202 delete elem; 203 } 204 205 --fCount; 206 } 207 208 }; 209 210 #endif 211