• 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 "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