/*
|
* 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 <typename T,
|
typename Key,
|
typename HashTraits=T>
|
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<ValueList, Key>::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<class FindPredicate>
|
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<class FindPredicate>
|
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<ValueList, Key>::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<ValueList, Key> 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
|