OLD | NEW |
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #ifndef VM_HASH_MAP_H_ | 5 #ifndef VM_HASH_MAP_H_ |
6 #define VM_HASH_MAP_H_ | 6 #define VM_HASH_MAP_H_ |
7 | 7 |
| 8 #include "vm/growable_array.h" // For Malloc, EmptyBase |
8 #include "vm/zone.h" | 9 #include "vm/zone.h" |
9 | 10 |
10 namespace dart { | 11 namespace dart { |
11 | 12 |
12 template <typename KeyValueTrait> | 13 template<typename KeyValueTrait, typename B, typename Allocator = Zone> |
13 class DirectChainedHashMap: public ValueObject { | 14 class BaseDirectChainedHashMap : public B { |
14 public: | 15 public: |
15 DirectChainedHashMap() : array_size_(0), | 16 explicit BaseDirectChainedHashMap(Allocator* allocator) |
16 lists_size_(0), | 17 : array_size_(0), |
17 count_(0), | 18 lists_size_(0), |
18 array_(NULL), | 19 count_(0), |
19 lists_(NULL), | 20 array_(NULL), |
20 free_list_head_(kNil) { | 21 lists_(NULL), |
| 22 free_list_head_(kNil), |
| 23 allocator_(allocator) { |
21 ResizeLists(kInitialSize); | 24 ResizeLists(kInitialSize); |
22 Resize(kInitialSize); | 25 Resize(kInitialSize); |
23 } | 26 } |
24 | 27 |
25 DirectChainedHashMap(const DirectChainedHashMap& other); | 28 BaseDirectChainedHashMap(const BaseDirectChainedHashMap& other); |
| 29 |
| 30 ~BaseDirectChainedHashMap() { |
| 31 allocator_->template Free<HashMapListElement>(array_, array_size_); |
| 32 allocator_->template Free<HashMapListElement>(lists_, lists_size_); |
| 33 } |
26 | 34 |
27 void Insert(typename KeyValueTrait::Pair kv); | 35 void Insert(typename KeyValueTrait::Pair kv); |
28 | 36 |
29 typename KeyValueTrait::Value Lookup(typename KeyValueTrait::Key key) const; | 37 typename KeyValueTrait::Value LookupValue( |
| 38 typename KeyValueTrait::Key key) const; |
| 39 |
| 40 typename KeyValueTrait::Pair* Lookup(typename KeyValueTrait::Key key) const; |
30 | 41 |
31 bool IsEmpty() const { return count_ == 0; } | 42 bool IsEmpty() const { return count_ == 0; } |
32 | 43 |
33 void Clear() { | 44 void Clear() { |
34 if (!IsEmpty()) { | 45 if (!IsEmpty()) { |
35 count_ = 0; | 46 count_ = 0; |
36 InitArray(array_, array_size_); | 47 InitArray(array_, array_size_); |
37 InitArray(lists_, lists_size_); | 48 InitArray(lists_, lists_size_); |
38 lists_[0].next = kNil; | 49 lists_[0].next = kNil; |
39 for (intptr_t i = 1; i < lists_size_; ++i) { | 50 for (intptr_t i = 1; i < lists_size_; ++i) { |
40 lists_[i].next = i - 1; | 51 lists_[i].next = i - 1; |
41 } | 52 } |
42 free_list_head_ = lists_size_ - 1; | 53 free_list_head_ = lists_size_ - 1; |
43 } | 54 } |
44 } | 55 } |
45 | 56 |
| 57 class Iterator { |
| 58 public: |
| 59 typename KeyValueTrait::Pair* Next(); |
| 60 |
| 61 void Reset() { |
| 62 array_index_ = 0; |
| 63 list_index_ = kNil; |
| 64 } |
| 65 |
| 66 private: |
| 67 explicit Iterator(const BaseDirectChainedHashMap& map) |
| 68 : map_(map), array_index_(0), list_index_(kNil) {} |
| 69 |
| 70 const BaseDirectChainedHashMap& map_; |
| 71 intptr_t array_index_; |
| 72 intptr_t list_index_; |
| 73 |
| 74 template<typename T, typename Bs, typename A> |
| 75 friend class BaseDirectChainedHashMap; |
| 76 }; |
| 77 |
| 78 Iterator GetIterator() const { return Iterator(*this); } |
| 79 |
46 protected: | 80 protected: |
47 // A linked list of T values. Stored in arrays. | 81 // A linked list of T values. Stored in arrays. |
48 struct HashMapListElement { | 82 struct HashMapListElement { |
49 HashMapListElement() : kv(), next(kNil) { } | 83 HashMapListElement() : kv(), next(kNil) { } |
50 typename KeyValueTrait::Pair kv; | 84 typename KeyValueTrait::Pair kv; |
51 intptr_t next; // Index in the array of the next list element. | 85 intptr_t next; // Index in the array of the next list element. |
52 }; | 86 }; |
53 static const intptr_t kNil = -1; // The end of a linked list | 87 static const intptr_t kNil = -1; // The end of a linked list |
54 | 88 |
55 static void InitArray(HashMapListElement* array, intptr_t size) { | 89 static void InitArray(HashMapListElement* array, intptr_t size) { |
56 for (intptr_t i = 0; i < size; ++i) { | 90 for (intptr_t i = 0; i < size; ++i) { |
57 array[i] = HashMapListElement(); | 91 array[i] = HashMapListElement(); |
58 } | 92 } |
59 } | 93 } |
60 | 94 |
61 // Must be a power of 2. | 95 // Must be a power of 2. |
62 static const intptr_t kInitialSize = 16; | 96 static const intptr_t kInitialSize = 16; |
63 | 97 |
64 void Resize(intptr_t new_size); | 98 void Resize(intptr_t new_size); |
65 void ResizeLists(intptr_t new_size); | 99 void ResizeLists(intptr_t new_size); |
66 uword Bound(uword value) const { return value & (array_size_ - 1); } | 100 uword Bound(uword value) const { return value & (array_size_ - 1); } |
67 | 101 |
68 intptr_t array_size_; | 102 intptr_t array_size_; |
69 intptr_t lists_size_; | 103 intptr_t lists_size_; |
70 intptr_t count_; // The number of values stored in the HashMap. | 104 intptr_t count_; // The number of values stored in the HashMap. |
71 HashMapListElement* array_; // Primary store - contains the first value | 105 HashMapListElement* array_; // Primary store - contains the first value |
72 // with a given hash. Colliding elements are stored in linked lists. | 106 // with a given hash. Colliding elements are stored in linked lists. |
73 HashMapListElement* lists_; // The linked lists containing hash collisions. | 107 HashMapListElement* lists_; // The linked lists containing hash collisions. |
74 intptr_t free_list_head_; // Unused elements in lists_ are on the free list. | 108 intptr_t free_list_head_; // Unused elements in lists_ are on the free list. |
| 109 Allocator* allocator_; |
75 }; | 110 }; |
76 | 111 |
77 | 112 |
78 template <typename KeyValueTrait> | 113 template<typename KeyValueTrait, typename B, typename Allocator> |
79 typename KeyValueTrait::Value | 114 BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>:: |
80 DirectChainedHashMap<KeyValueTrait>:: | 115 BaseDirectChainedHashMap(const BaseDirectChainedHashMap& other) |
| 116 : B(), |
| 117 array_size_(other.array_size_), |
| 118 lists_size_(other.lists_size_), |
| 119 count_(other.count_), |
| 120 array_(other.allocator_->template Alloc<HashMapListElement>( |
| 121 other.array_size_)), |
| 122 lists_(other.allocator_->template Alloc<HashMapListElement>( |
| 123 other.lists_size_)), |
| 124 free_list_head_(other.free_list_head_), |
| 125 allocator_(other.allocator_) { |
| 126 memmove(array_, other.array_, array_size_ * sizeof(HashMapListElement)); |
| 127 memmove(lists_, other.lists_, lists_size_ * sizeof(HashMapListElement)); |
| 128 } |
| 129 |
| 130 |
| 131 template<typename KeyValueTrait, typename B, typename Allocator> |
| 132 typename KeyValueTrait::Pair* |
| 133 BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>:: |
81 Lookup(typename KeyValueTrait::Key key) const { | 134 Lookup(typename KeyValueTrait::Key key) const { |
82 const typename KeyValueTrait::Value kNoValue = | 135 const typename KeyValueTrait::Value kNoValue = |
83 KeyValueTrait::ValueOf(typename KeyValueTrait::Pair()); | 136 KeyValueTrait::ValueOf(typename KeyValueTrait::Pair()); |
84 | 137 |
85 uword hash = static_cast<uword>(KeyValueTrait::Hashcode(key)); | 138 uword hash = static_cast<uword>(KeyValueTrait::Hashcode(key)); |
86 uword pos = Bound(hash); | 139 uword pos = Bound(hash); |
87 if (KeyValueTrait::ValueOf(array_[pos].kv) != kNoValue) { | 140 if (KeyValueTrait::ValueOf(array_[pos].kv) != kNoValue) { |
88 if (KeyValueTrait::IsKeyEqual(array_[pos].kv, key)) { | 141 if (KeyValueTrait::IsKeyEqual(array_[pos].kv, key)) { |
89 return KeyValueTrait::ValueOf(array_[pos].kv); | 142 return &array_[pos].kv; |
90 } | 143 } |
91 | 144 |
92 intptr_t next = array_[pos].next; | 145 intptr_t next = array_[pos].next; |
93 while (next != kNil) { | 146 while (next != kNil) { |
94 if (KeyValueTrait::IsKeyEqual(lists_[next].kv, key)) { | 147 if (KeyValueTrait::IsKeyEqual(lists_[next].kv, key)) { |
95 return KeyValueTrait::ValueOf(lists_[next].kv); | 148 return &lists_[next].kv; |
96 } | 149 } |
97 next = lists_[next].next; | 150 next = lists_[next].next; |
98 } | 151 } |
99 } | 152 } |
100 return kNoValue; | 153 return NULL; |
101 } | 154 } |
102 | 155 |
103 | 156 |
104 template <typename KeyValueTrait> | 157 template<typename KeyValueTrait, typename B, typename Allocator> |
105 DirectChainedHashMap<KeyValueTrait>:: | 158 typename KeyValueTrait::Value |
106 DirectChainedHashMap(const DirectChainedHashMap& other) | 159 BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>:: |
107 : ValueObject(), | 160 LookupValue(typename KeyValueTrait::Key key) const { |
108 array_size_(other.array_size_), | 161 const typename KeyValueTrait::Value kNoValue = |
109 lists_size_(other.lists_size_), | 162 KeyValueTrait::ValueOf(typename KeyValueTrait::Pair()); |
110 count_(other.count_), | 163 typename KeyValueTrait::Pair* pair = Lookup(key); |
111 array_(Thread::Current()->zone()-> | 164 return (pair == NULL) ? kNoValue : KeyValueTrait::ValueOf(*pair); |
112 Alloc<HashMapListElement>(other.array_size_)), | |
113 lists_(Thread::Current()->zone()-> | |
114 Alloc<HashMapListElement>(other.lists_size_)), | |
115 free_list_head_(other.free_list_head_) { | |
116 memmove(array_, other.array_, array_size_ * sizeof(HashMapListElement)); | |
117 memmove(lists_, other.lists_, lists_size_ * sizeof(HashMapListElement)); | |
118 } | 165 } |
119 | 166 |
120 | 167 |
121 template <typename KeyValueTrait> | 168 template<typename KeyValueTrait, typename B, typename Allocator> |
122 void DirectChainedHashMap<KeyValueTrait>::Resize(intptr_t new_size) { | 169 typename KeyValueTrait::Pair* |
| 170 BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Iterator::Next() { |
| 171 const typename KeyValueTrait::Pair kNoPair = typename KeyValueTrait::Pair(); |
| 172 |
| 173 if (array_index_ < map_.array_size_) { |
| 174 // If we're not in the middle of a list, find the next array slot. |
| 175 if (list_index_ == kNil) { |
| 176 while ((map_.array_[array_index_].kv == kNoPair) && |
| 177 (array_index_ < map_.array_size_)) { |
| 178 array_index_++; |
| 179 } |
| 180 if (array_index_ < map_.array_size_) { |
| 181 // When we're done with the list, we'll continue with the next array |
| 182 // slot. |
| 183 const intptr_t old_array_index = array_index_; |
| 184 array_index_++; |
| 185 list_index_ = map_.array_[old_array_index].next; |
| 186 return &map_.array_[old_array_index].kv; |
| 187 } else { |
| 188 return NULL; |
| 189 } |
| 190 } |
| 191 |
| 192 // Otherwise, return the current lists_ entry, advancing list_index_. |
| 193 intptr_t current = list_index_; |
| 194 list_index_ = map_.lists_[current].next; |
| 195 return &map_.lists_[current].kv; |
| 196 } |
| 197 |
| 198 return NULL; |
| 199 } |
| 200 |
| 201 |
| 202 template<typename KeyValueTrait, typename B, typename Allocator> |
| 203 void BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::Resize( |
| 204 intptr_t new_size) { |
123 const typename KeyValueTrait::Value kNoValue = | 205 const typename KeyValueTrait::Value kNoValue = |
124 KeyValueTrait::ValueOf(typename KeyValueTrait::Pair()); | 206 KeyValueTrait::ValueOf(typename KeyValueTrait::Pair()); |
125 | 207 |
126 ASSERT(new_size > count_); | 208 ASSERT(new_size > count_); |
127 // Hashing the values into the new array has no more collisions than in the | 209 // Hashing the values into the new array has no more collisions than in the |
128 // old hash map, so we can use the existing lists_ array, if we are careful. | 210 // old hash map, so we can use the existing lists_ array, if we are careful. |
129 | 211 |
130 // Make sure we have at least one free element. | 212 // Make sure we have at least one free element. |
131 if (free_list_head_ == kNil) { | 213 if (free_list_head_ == kNil) { |
132 ResizeLists(lists_size_ << 1); | 214 ResizeLists(lists_size_ << 1); |
133 } | 215 } |
134 | 216 |
135 HashMapListElement* new_array = | 217 HashMapListElement* new_array = |
136 Thread::Current()->zone()->Alloc<HashMapListElement>(new_size); | 218 allocator_->template Alloc<HashMapListElement>(new_size); |
137 InitArray(new_array, new_size); | 219 InitArray(new_array, new_size); |
138 | 220 |
139 HashMapListElement* old_array = array_; | 221 HashMapListElement* old_array = array_; |
140 intptr_t old_size = array_size_; | 222 intptr_t old_size = array_size_; |
141 | 223 |
142 intptr_t old_count = count_; | 224 intptr_t old_count = count_; |
143 count_ = 0; | 225 count_ = 0; |
144 array_size_ = new_size; | 226 array_size_ = new_size; |
145 array_ = new_array; | 227 array_ = new_array; |
146 | 228 |
147 if (old_array != NULL) { | 229 if (old_array != NULL) { |
148 // Iterate over all the elements in lists, rehashing them. | 230 // Iterate over all the elements in lists, rehashing them. |
149 for (intptr_t i = 0; i < old_size; ++i) { | 231 for (intptr_t i = 0; i < old_size; ++i) { |
150 if (KeyValueTrait::ValueOf(old_array[i].kv) != kNoValue) { | 232 if (KeyValueTrait::ValueOf(old_array[i].kv) != kNoValue) { |
151 intptr_t current = old_array[i].next; | 233 intptr_t current = old_array[i].next; |
152 while (current != kNil) { | 234 while (current != kNil) { |
153 Insert(lists_[current].kv); | 235 Insert(lists_[current].kv); |
154 intptr_t next = lists_[current].next; | 236 intptr_t next = lists_[current].next; |
155 lists_[current].next = free_list_head_; | 237 lists_[current].next = free_list_head_; |
156 free_list_head_ = current; | 238 free_list_head_ = current; |
157 current = next; | 239 current = next; |
158 } | 240 } |
159 // Rehash the directly stored value. | 241 // Rehash the directly stored value. |
160 Insert(old_array[i].kv); | 242 Insert(old_array[i].kv); |
161 } | 243 } |
162 } | 244 } |
163 } | 245 } |
164 USE(old_count); | 246 USE(old_count); |
165 ASSERT(count_ == old_count); | 247 ASSERT(count_ == old_count); |
| 248 allocator_->template Free<HashMapListElement>(old_array, old_size); |
166 } | 249 } |
167 | 250 |
168 | 251 |
169 template <typename T> | 252 template<typename KeyValueTrait, typename B, typename Allocator> |
170 void DirectChainedHashMap<T>::ResizeLists(intptr_t new_size) { | 253 void BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>::ResizeLists( |
| 254 intptr_t new_size) { |
171 ASSERT(new_size > lists_size_); | 255 ASSERT(new_size > lists_size_); |
172 | 256 |
173 HashMapListElement* new_lists = | 257 HashMapListElement* new_lists = |
174 Thread::Current()->zone()-> | 258 allocator_->template Alloc<HashMapListElement>(new_size); |
175 Alloc<HashMapListElement>(new_size); | |
176 InitArray(new_lists, new_size); | 259 InitArray(new_lists, new_size); |
177 | 260 |
178 HashMapListElement* old_lists = lists_; | 261 HashMapListElement* old_lists = lists_; |
179 intptr_t old_size = lists_size_; | 262 intptr_t old_size = lists_size_; |
180 | 263 |
181 lists_size_ = new_size; | 264 lists_size_ = new_size; |
182 lists_ = new_lists; | 265 lists_ = new_lists; |
183 | 266 |
184 if (old_lists != NULL) { | 267 if (old_lists != NULL) { |
185 memmove(lists_, old_lists, old_size * sizeof(HashMapListElement)); | 268 memmove(lists_, old_lists, old_size * sizeof(HashMapListElement)); |
186 } | 269 } |
187 for (intptr_t i = old_size; i < lists_size_; ++i) { | 270 for (intptr_t i = old_size; i < lists_size_; ++i) { |
188 lists_[i].next = free_list_head_; | 271 lists_[i].next = free_list_head_; |
189 free_list_head_ = i; | 272 free_list_head_ = i; |
190 } | 273 } |
| 274 allocator_->template Free<HashMapListElement>(old_lists, old_size); |
191 } | 275 } |
192 | 276 |
193 | 277 |
194 template <typename KeyValueTrait> | 278 template<typename KeyValueTrait, typename B, typename Allocator> |
195 void DirectChainedHashMap<KeyValueTrait>:: | 279 void BaseDirectChainedHashMap<KeyValueTrait, B, Allocator>:: |
196 Insert(typename KeyValueTrait::Pair kv) { | 280 Insert(typename KeyValueTrait::Pair kv) { |
197 const typename KeyValueTrait::Value kNoValue = | 281 const typename KeyValueTrait::Value kNoValue = |
198 KeyValueTrait::ValueOf(typename KeyValueTrait::Pair()); | 282 KeyValueTrait::ValueOf(typename KeyValueTrait::Pair()); |
199 | 283 |
200 ASSERT(KeyValueTrait::ValueOf(kv) != kNoValue); | 284 ASSERT(KeyValueTrait::ValueOf(kv) != kNoValue); |
201 // Resizing when half of the hashtable is filled up. | 285 // Resizing when half of the hashtable is filled up. |
202 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1); | 286 if (count_ >= array_size_ >> 1) Resize(array_size_ << 1); |
203 ASSERT(count_ < array_size_); | 287 ASSERT(count_ < array_size_); |
204 count_++; | 288 count_++; |
205 uword pos = Bound( | 289 uword pos = Bound( |
(...skipping 10 matching lines...) Expand all Loading... |
216 free_list_head_ = lists_[free_list_head_].next; | 300 free_list_head_ = lists_[free_list_head_].next; |
217 lists_[new_element_pos].kv = kv; | 301 lists_[new_element_pos].kv = kv; |
218 lists_[new_element_pos].next = array_[pos].next; | 302 lists_[new_element_pos].next = array_[pos].next; |
219 ASSERT(array_[pos].next == kNil || | 303 ASSERT(array_[pos].next == kNil || |
220 KeyValueTrait::ValueOf(lists_[array_[pos].next].kv) != kNoValue); | 304 KeyValueTrait::ValueOf(lists_[array_[pos].next].kv) != kNoValue); |
221 array_[pos].next = new_element_pos; | 305 array_[pos].next = new_element_pos; |
222 } | 306 } |
223 } | 307 } |
224 | 308 |
225 | 309 |
| 310 template<typename KeyValueTrait> |
| 311 class DirectChainedHashMap |
| 312 : public BaseDirectChainedHashMap<KeyValueTrait, ValueObject> { |
| 313 public: |
| 314 DirectChainedHashMap() : BaseDirectChainedHashMap<KeyValueTrait, ValueObject>( |
| 315 ASSERT_NOTNULL(Thread::Current()->zone())) {} |
| 316 }; |
| 317 |
| 318 |
| 319 template<typename KeyValueTrait> |
| 320 class MallocDirectChainedHashMap |
| 321 : public BaseDirectChainedHashMap<KeyValueTrait, EmptyBase, Malloc> { |
| 322 public: |
| 323 MallocDirectChainedHashMap() |
| 324 : BaseDirectChainedHashMap<KeyValueTrait, EmptyBase, Malloc>(NULL) {} |
| 325 }; |
| 326 |
| 327 |
226 template<typename T> | 328 template<typename T> |
227 class PointerKeyValueTrait { | 329 class PointerKeyValueTrait { |
228 public: | 330 public: |
229 typedef T* Value; | 331 typedef T* Value; |
230 typedef T* Key; | 332 typedef T* Key; |
231 typedef T* Pair; | 333 typedef T* Pair; |
232 | 334 |
233 static Key KeyOf(Pair kv) { | 335 static Key KeyOf(Pair kv) { |
234 return kv; | 336 return kv; |
235 } | 337 } |
236 | 338 |
237 static Value ValueOf(Pair kv) { | 339 static Value ValueOf(Pair kv) { |
238 return kv; | 340 return kv; |
239 } | 341 } |
240 | 342 |
241 static inline intptr_t Hashcode(Key key) { | 343 static inline intptr_t Hashcode(Key key) { |
242 return key->Hashcode(); | 344 return key->Hashcode(); |
243 } | 345 } |
244 | 346 |
245 static inline bool IsKeyEqual(Pair kv, Key key) { | 347 static inline bool IsKeyEqual(Pair kv, Key key) { |
246 return kv->Equals(key); | 348 return kv->Equals(key); |
247 } | 349 } |
248 }; | 350 }; |
249 | 351 |
| 352 |
| 353 template<typename T> |
| 354 class NumbersKeyValueTrait { |
| 355 public: |
| 356 typedef T Value; |
| 357 typedef intptr_t Key; |
| 358 typedef T Pair; |
| 359 |
| 360 static intptr_t KeyOf(Pair kv) { return kv.first(); } |
| 361 static T ValueOf(Pair kv) { return kv; } |
| 362 static inline intptr_t Hashcode(Key key) { return key; } |
| 363 static inline bool IsKeyEqual(Pair kv, Key key) { return kv.first() == key; } |
| 364 }; |
| 365 |
250 } // namespace dart | 366 } // namespace dart |
251 | 367 |
252 #endif // VM_HASH_MAP_H_ | 368 #endif // VM_HASH_MAP_H_ |
OLD | NEW |