• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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