/* * Copyright 2013 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkTMultiMap_DEFINED #define SkTMultiMap_DEFINED #include "GrTypes.h" #include "SkTDynamicHash.h" /** A set that contains pointers to instances of T. Instances can be looked up with key Key. * Multiple (possibly same) values can have the same key. */ template class SkTMultiMap { struct ValueList { explicit ValueList(T* value) : fValue(value), fNext(nullptr) {} static const Key& GetKey(const ValueList& e) { return HashTraits::GetKey(*e.fValue); } static uint32_t Hash(const Key& key) { return HashTraits::Hash(key); } T* fValue; ValueList* fNext; }; public: SkTMultiMap() : fCount(0) {} ~SkTMultiMap() { typename SkTDynamicHash::Iter iter(&fHash); for ( ; !iter.done(); ++iter) { ValueList* next; for (ValueList* cur = &(*iter); cur; cur = next) { HashTraits::OnFree(cur->fValue); next = cur->fNext; delete cur; } } } void insert(const Key& key, T* value) { ValueList* list = fHash.find(key); if (list) { // The new ValueList entry is inserted as the second element in the // linked list, and it will contain the value of the first element. ValueList* newEntry = new ValueList(list->fValue); newEntry->fNext = list->fNext; // The existing first ValueList entry is updated to contain the // inserted value. list->fNext = newEntry; list->fValue = value; } else { fHash.add(new ValueList(value)); } ++fCount; } void remove(const Key& key, const T* value) { ValueList* list = fHash.find(key); // Temporarily making this safe for remove entries not in the map because of // crbug.com/877915. #if 0 // Since we expect the caller to be fully aware of what is stored, just // assert that the caller removes an existing value. SkASSERT(list); ValueList* prev = nullptr; while (list->fValue != value) { prev = list; list = list->fNext; } this->internalRemove(prev, list, key); #else ValueList* prev = nullptr; while (list && list->fValue != value) { prev = list; list = list->fNext; } // Crash in Debug since it'd be great to detect a repro of 877915. SkASSERT(list); if (list) { this->internalRemove(prev, list, key); } #endif } T* find(const Key& key) const { ValueList* list = fHash.find(key); if (list) { return list->fValue; } return nullptr; } template T* find(const Key& key, const FindPredicate f) { ValueList* list = fHash.find(key); while (list) { if (f(list->fValue)){ return list->fValue; } list = list->fNext; } return nullptr; } template T* findAndRemove(const Key& key, const FindPredicate f) { ValueList* list = fHash.find(key); ValueList* prev = nullptr; while (list) { if (f(list->fValue)){ T* value = list->fValue; this->internalRemove(prev, list, key); return value; } prev = list; list = list->fNext; } return nullptr; } int count() const { return fCount; } #ifdef SK_DEBUG class ConstIter { public: explicit ConstIter(const SkTMultiMap* mmap) : fIter(&(mmap->fHash)) , fList(nullptr) { if (!fIter.done()) { fList = &(*fIter); } } bool done() const { return fIter.done(); } const T* operator*() { SkASSERT(fList); return fList->fValue; } void operator++() { if (fList) { fList = fList->fNext; } if (!fList) { ++fIter; if (!fIter.done()) { fList = &(*fIter); } } } private: typename SkTDynamicHash::ConstIter fIter; const ValueList* fList; }; bool has(const T* value, const Key& key) const { for (ValueList* list = fHash.find(key); list; list = list->fNext) { if (list->fValue == value) { return true; } } return false; } // This is not particularly fast and only used for validation, so debug only. int countForKey(const Key& key) const { int count = 0; ValueList* list = fHash.find(key); while (list) { list = list->fNext; ++count; } return count; } #endif private: SkTDynamicHash fHash; int fCount; void internalRemove(ValueList* prev, ValueList* elem, const Key& key) { if (elem->fNext) { ValueList* next = elem->fNext; elem->fValue = next->fValue; elem->fNext = next->fNext; delete next; } else if (prev) { prev->fNext = nullptr; delete elem; } else { fHash.remove(key); delete elem; } --fCount; } }; #endif