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