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