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