| OLD | NEW |
| (Empty) |
| 1 /* | |
| 2 * Copyright (C) 2011 Google Inc. All rights reserved. | |
| 3 * | |
| 4 * Redistribution and use in source and binary forms, with or without | |
| 5 * modification, are permitted provided that the following conditions | |
| 6 * are met: | |
| 7 * 1. Redistributions of source code must retain the above copyright | |
| 8 * notice, this list of conditions and the following disclaimer. | |
| 9 * 2. Redistributions in binary form must reproduce the above copyright | |
| 10 * notice, this list of conditions and the following disclaimer in the | |
| 11 * documentation and/or other materials provided with the distribution. | |
| 12 * | |
| 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' | |
| 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, | |
| 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | |
| 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS | |
| 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF | |
| 23 * THE POSSIBILITY OF SUCH DAMAGE. | |
| 24 */ | |
| 25 | |
| 26 #include "wtf/HashMap.h" | |
| 27 | |
| 28 #include "testing/gtest/include/gtest/gtest.h" | |
| 29 #include "wtf/PassRefPtr.h" | |
| 30 #include "wtf/PtrUtil.h" | |
| 31 #include "wtf/RefCounted.h" | |
| 32 #include "wtf/Vector.h" | |
| 33 #include <memory> | |
| 34 | |
| 35 namespace WTF { | |
| 36 | |
| 37 namespace { | |
| 38 | |
| 39 using IntHashMap = HashMap<int, int>; | |
| 40 | |
| 41 TEST(HashMapTest, IteratorComparison) { | |
| 42 IntHashMap map; | |
| 43 map.insert(1, 2); | |
| 44 EXPECT_TRUE(map.begin() != map.end()); | |
| 45 EXPECT_FALSE(map.begin() == map.end()); | |
| 46 | |
| 47 IntHashMap::const_iterator begin = map.begin(); | |
| 48 EXPECT_TRUE(begin == map.begin()); | |
| 49 EXPECT_TRUE(map.begin() == begin); | |
| 50 EXPECT_TRUE(begin != map.end()); | |
| 51 EXPECT_TRUE(map.end() != begin); | |
| 52 EXPECT_FALSE(begin != map.begin()); | |
| 53 EXPECT_FALSE(map.begin() != begin); | |
| 54 EXPECT_FALSE(begin == map.end()); | |
| 55 EXPECT_FALSE(map.end() == begin); | |
| 56 } | |
| 57 | |
| 58 struct TestDoubleHashTraits : HashTraits<double> { | |
| 59 static const unsigned minimumTableSize = 8; | |
| 60 }; | |
| 61 | |
| 62 using DoubleHashMap = | |
| 63 HashMap<double, int64_t, DefaultHash<double>::Hash, TestDoubleHashTraits>; | |
| 64 | |
| 65 int bucketForKey(double key) { | |
| 66 return DefaultHash<double>::Hash::hash(key) & | |
| 67 (TestDoubleHashTraits::minimumTableSize - 1); | |
| 68 } | |
| 69 | |
| 70 TEST(HashMapTest, DoubleHashCollisions) { | |
| 71 // The "clobber" key here is one that ends up stealing the bucket that the -0 | |
| 72 // key originally wants to be in. This makes the 0 and -0 keys collide and | |
| 73 // the test then fails unless the FloatHash::equals() implementation can | |
| 74 // distinguish them. | |
| 75 const double clobberKey = 6; | |
| 76 const double zeroKey = 0; | |
| 77 const double negativeZeroKey = -zeroKey; | |
| 78 | |
| 79 DoubleHashMap map; | |
| 80 | |
| 81 map.insert(clobberKey, 1); | |
| 82 map.insert(zeroKey, 2); | |
| 83 map.insert(negativeZeroKey, 3); | |
| 84 | |
| 85 EXPECT_EQ(bucketForKey(clobberKey), bucketForKey(negativeZeroKey)); | |
| 86 EXPECT_EQ(1, map.at(clobberKey)); | |
| 87 EXPECT_EQ(2, map.at(zeroKey)); | |
| 88 EXPECT_EQ(3, map.at(negativeZeroKey)); | |
| 89 } | |
| 90 | |
| 91 class DestructCounter { | |
| 92 public: | |
| 93 explicit DestructCounter(int i, int* destructNumber) | |
| 94 : m_i(i), m_destructNumber(destructNumber) {} | |
| 95 | |
| 96 ~DestructCounter() { ++(*m_destructNumber); } | |
| 97 int get() const { return m_i; } | |
| 98 | |
| 99 private: | |
| 100 int m_i; | |
| 101 int* m_destructNumber; | |
| 102 }; | |
| 103 | |
| 104 using OwnPtrHashMap = HashMap<int, std::unique_ptr<DestructCounter>>; | |
| 105 | |
| 106 TEST(HashMapTest, OwnPtrAsValue) { | |
| 107 int destructNumber = 0; | |
| 108 OwnPtrHashMap map; | |
| 109 map.insert(1, WTF::wrapUnique(new DestructCounter(1, &destructNumber))); | |
| 110 map.insert(2, WTF::wrapUnique(new DestructCounter(2, &destructNumber))); | |
| 111 | |
| 112 DestructCounter* counter1 = map.at(1); | |
| 113 EXPECT_EQ(1, counter1->get()); | |
| 114 DestructCounter* counter2 = map.at(2); | |
| 115 EXPECT_EQ(2, counter2->get()); | |
| 116 EXPECT_EQ(0, destructNumber); | |
| 117 | |
| 118 for (OwnPtrHashMap::iterator iter = map.begin(); iter != map.end(); ++iter) { | |
| 119 std::unique_ptr<DestructCounter>& ownCounter = iter->value; | |
| 120 EXPECT_EQ(iter->key, ownCounter->get()); | |
| 121 } | |
| 122 ASSERT_EQ(0, destructNumber); | |
| 123 | |
| 124 std::unique_ptr<DestructCounter> ownCounter1 = map.take(1); | |
| 125 EXPECT_EQ(ownCounter1.get(), counter1); | |
| 126 EXPECT_FALSE(map.contains(1)); | |
| 127 EXPECT_EQ(0, destructNumber); | |
| 128 | |
| 129 map.erase(2); | |
| 130 EXPECT_FALSE(map.contains(2)); | |
| 131 EXPECT_EQ(0UL, map.size()); | |
| 132 EXPECT_EQ(1, destructNumber); | |
| 133 | |
| 134 ownCounter1.reset(); | |
| 135 EXPECT_EQ(2, destructNumber); | |
| 136 } | |
| 137 | |
| 138 class DummyRefCounted : public RefCounted<DummyRefCounted> { | |
| 139 public: | |
| 140 DummyRefCounted(bool& isDeleted) : m_isDeleted(isDeleted) { | |
| 141 m_isDeleted = false; | |
| 142 } | |
| 143 ~DummyRefCounted() { | |
| 144 DCHECK(!m_isDeleted); | |
| 145 m_isDeleted = true; | |
| 146 } | |
| 147 | |
| 148 void ref() { | |
| 149 DCHECK(!m_isDeleted); | |
| 150 WTF::RefCounted<DummyRefCounted>::ref(); | |
| 151 ++m_refInvokesCount; | |
| 152 } | |
| 153 | |
| 154 void deref() { | |
| 155 DCHECK(!m_isDeleted); | |
| 156 WTF::RefCounted<DummyRefCounted>::deref(); | |
| 157 } | |
| 158 | |
| 159 static int m_refInvokesCount; | |
| 160 | |
| 161 private: | |
| 162 bool& m_isDeleted; | |
| 163 }; | |
| 164 | |
| 165 int DummyRefCounted::m_refInvokesCount = 0; | |
| 166 | |
| 167 TEST(HashMapTest, RefPtrAsKey) { | |
| 168 bool isDeleted = false; | |
| 169 DummyRefCounted::m_refInvokesCount = 0; | |
| 170 RefPtr<DummyRefCounted> ptr = adoptRef(new DummyRefCounted(isDeleted)); | |
| 171 EXPECT_EQ(0, DummyRefCounted::m_refInvokesCount); | |
| 172 HashMap<RefPtr<DummyRefCounted>, int> map; | |
| 173 map.insert(ptr, 1); | |
| 174 // Referenced only once (to store a copy in the container). | |
| 175 EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); | |
| 176 EXPECT_EQ(1, map.at(ptr)); | |
| 177 | |
| 178 DummyRefCounted* rawPtr = ptr.get(); | |
| 179 | |
| 180 EXPECT_TRUE(map.contains(rawPtr)); | |
| 181 EXPECT_NE(map.end(), map.find(rawPtr)); | |
| 182 EXPECT_TRUE(map.contains(ptr)); | |
| 183 EXPECT_NE(map.end(), map.find(ptr)); | |
| 184 EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); | |
| 185 | |
| 186 ptr.clear(); | |
| 187 EXPECT_FALSE(isDeleted); | |
| 188 | |
| 189 map.erase(rawPtr); | |
| 190 EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); | |
| 191 EXPECT_TRUE(isDeleted); | |
| 192 EXPECT_TRUE(map.isEmpty()); | |
| 193 } | |
| 194 | |
| 195 TEST(HashMaptest, RemoveAdd) { | |
| 196 DummyRefCounted::m_refInvokesCount = 0; | |
| 197 bool isDeleted = false; | |
| 198 | |
| 199 typedef HashMap<int, RefPtr<DummyRefCounted>> Map; | |
| 200 Map map; | |
| 201 | |
| 202 RefPtr<DummyRefCounted> ptr = adoptRef(new DummyRefCounted(isDeleted)); | |
| 203 EXPECT_EQ(0, DummyRefCounted::m_refInvokesCount); | |
| 204 | |
| 205 map.insert(1, ptr); | |
| 206 // Referenced only once (to store a copy in the container). | |
| 207 EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); | |
| 208 EXPECT_EQ(ptr, map.at(1)); | |
| 209 | |
| 210 ptr.clear(); | |
| 211 EXPECT_FALSE(isDeleted); | |
| 212 | |
| 213 map.erase(1); | |
| 214 EXPECT_EQ(1, DummyRefCounted::m_refInvokesCount); | |
| 215 EXPECT_TRUE(isDeleted); | |
| 216 EXPECT_TRUE(map.isEmpty()); | |
| 217 | |
| 218 // Add and remove until the deleted slot is reused. | |
| 219 for (int i = 1; i < 100; i++) { | |
| 220 bool isDeleted2 = false; | |
| 221 RefPtr<DummyRefCounted> ptr2 = adoptRef(new DummyRefCounted(isDeleted2)); | |
| 222 map.insert(i, ptr2); | |
| 223 EXPECT_FALSE(isDeleted2); | |
| 224 ptr2.clear(); | |
| 225 EXPECT_FALSE(isDeleted2); | |
| 226 map.erase(i); | |
| 227 EXPECT_TRUE(isDeleted2); | |
| 228 } | |
| 229 } | |
| 230 | |
| 231 class SimpleClass { | |
| 232 public: | |
| 233 explicit SimpleClass(int v) : m_v(v) {} | |
| 234 int v() { return m_v; } | |
| 235 | |
| 236 private: | |
| 237 int m_v; | |
| 238 }; | |
| 239 using IntSimpleMap = HashMap<int, std::unique_ptr<SimpleClass>>; | |
| 240 | |
| 241 TEST(HashMapTest, AddResult) { | |
| 242 IntSimpleMap map; | |
| 243 IntSimpleMap::AddResult result = map.insert(1, nullptr); | |
| 244 EXPECT_TRUE(result.isNewEntry); | |
| 245 EXPECT_EQ(1, result.storedValue->key); | |
| 246 EXPECT_EQ(0, result.storedValue->value.get()); | |
| 247 | |
| 248 SimpleClass* simple1 = new SimpleClass(1); | |
| 249 result.storedValue->value = WTF::wrapUnique(simple1); | |
| 250 EXPECT_EQ(simple1, map.at(1)); | |
| 251 | |
| 252 IntSimpleMap::AddResult result2 = | |
| 253 map.insert(1, WTF::makeUnique<SimpleClass>(2)); | |
| 254 EXPECT_FALSE(result2.isNewEntry); | |
| 255 EXPECT_EQ(1, result.storedValue->key); | |
| 256 EXPECT_EQ(1, result.storedValue->value->v()); | |
| 257 EXPECT_EQ(1, map.at(1)->v()); | |
| 258 } | |
| 259 | |
| 260 TEST(HashMapTest, AddResultVectorValue) { | |
| 261 using IntVectorMap = HashMap<int, Vector<int>>; | |
| 262 IntVectorMap map; | |
| 263 IntVectorMap::AddResult result = map.insert(1, Vector<int>()); | |
| 264 EXPECT_TRUE(result.isNewEntry); | |
| 265 EXPECT_EQ(1, result.storedValue->key); | |
| 266 EXPECT_EQ(0u, result.storedValue->value.size()); | |
| 267 | |
| 268 result.storedValue->value.push_back(11); | |
| 269 EXPECT_EQ(1u, map.find(1)->value.size()); | |
| 270 EXPECT_EQ(11, map.find(1)->value.front()); | |
| 271 | |
| 272 IntVectorMap::AddResult result2 = map.insert(1, Vector<int>()); | |
| 273 EXPECT_FALSE(result2.isNewEntry); | |
| 274 EXPECT_EQ(1, result.storedValue->key); | |
| 275 EXPECT_EQ(1u, result.storedValue->value.size()); | |
| 276 EXPECT_EQ(11, result.storedValue->value.front()); | |
| 277 EXPECT_EQ(11, map.find(1)->value.front()); | |
| 278 } | |
| 279 | |
| 280 class InstanceCounter { | |
| 281 public: | |
| 282 InstanceCounter() { ++counter; } | |
| 283 InstanceCounter(const InstanceCounter& another) { ++counter; } | |
| 284 ~InstanceCounter() { --counter; } | |
| 285 static int counter; | |
| 286 }; | |
| 287 int InstanceCounter::counter = 0; | |
| 288 | |
| 289 TEST(HashMapTest, ValueTypeDestructed) { | |
| 290 InstanceCounter::counter = 0; | |
| 291 HashMap<int, InstanceCounter> map; | |
| 292 map.set(1, InstanceCounter()); | |
| 293 map.clear(); | |
| 294 EXPECT_EQ(0, InstanceCounter::counter); | |
| 295 } | |
| 296 | |
| 297 class MoveOnly { | |
| 298 public: | |
| 299 // kEmpty and kDeleted have special meanings when MoveOnly is used as the key | |
| 300 // of a hash table. | |
| 301 enum { kEmpty = 0, kDeleted = -1, kMovedOut = -2 }; | |
| 302 | |
| 303 explicit MoveOnly(int value = kEmpty) : m_value(value) {} | |
| 304 MoveOnly(MoveOnly&& other) : m_value(other.m_value) { | |
| 305 other.m_value = kMovedOut; | |
| 306 } | |
| 307 MoveOnly& operator=(MoveOnly&& other) { | |
| 308 m_value = other.m_value; | |
| 309 other.m_value = kMovedOut; | |
| 310 return *this; | |
| 311 } | |
| 312 | |
| 313 int value() const { return m_value; } | |
| 314 | |
| 315 private: | |
| 316 MoveOnly(const MoveOnly&) = delete; | |
| 317 MoveOnly& operator=(const MoveOnly&) = delete; | |
| 318 | |
| 319 int m_value; | |
| 320 }; | |
| 321 | |
| 322 struct MoveOnlyHashTraits : public GenericHashTraits<MoveOnly> { | |
| 323 // This is actually true, but we pretend that it's false to disable the | |
| 324 // optimization. | |
| 325 static const bool emptyValueIsZero = false; | |
| 326 | |
| 327 static const bool hasIsEmptyValueFunction = true; | |
| 328 static bool isEmptyValue(const MoveOnly& value) { | |
| 329 return value.value() == MoveOnly::kEmpty; | |
| 330 } | |
| 331 static void constructDeletedValue(MoveOnly& slot, bool) { | |
| 332 slot = MoveOnly(MoveOnly::kDeleted); | |
| 333 } | |
| 334 static bool isDeletedValue(const MoveOnly& value) { | |
| 335 return value.value() == MoveOnly::kDeleted; | |
| 336 } | |
| 337 }; | |
| 338 | |
| 339 struct MoveOnlyHash { | |
| 340 static unsigned hash(const MoveOnly& value) { | |
| 341 return DefaultHash<int>::Hash::hash(value.value()); | |
| 342 } | |
| 343 static bool equal(const MoveOnly& left, const MoveOnly& right) { | |
| 344 return DefaultHash<int>::Hash::equal(left.value(), right.value()); | |
| 345 } | |
| 346 static const bool safeToCompareToEmptyOrDeleted = true; | |
| 347 }; | |
| 348 | |
| 349 } // anonymous namespace | |
| 350 | |
| 351 template <> | |
| 352 struct HashTraits<MoveOnly> : public MoveOnlyHashTraits {}; | |
| 353 | |
| 354 template <> | |
| 355 struct DefaultHash<MoveOnly> { | |
| 356 using Hash = MoveOnlyHash; | |
| 357 }; | |
| 358 | |
| 359 namespace { | |
| 360 | |
| 361 TEST(HashMapTest, MoveOnlyValueType) { | |
| 362 using TheMap = HashMap<int, MoveOnly>; | |
| 363 TheMap map; | |
| 364 { | |
| 365 TheMap::AddResult addResult = map.insert(1, MoveOnly(10)); | |
| 366 EXPECT_TRUE(addResult.isNewEntry); | |
| 367 EXPECT_EQ(1, addResult.storedValue->key); | |
| 368 EXPECT_EQ(10, addResult.storedValue->value.value()); | |
| 369 } | |
| 370 auto iter = map.find(1); | |
| 371 ASSERT_TRUE(iter != map.end()); | |
| 372 EXPECT_EQ(1, iter->key); | |
| 373 EXPECT_EQ(10, iter->value.value()); | |
| 374 | |
| 375 iter = map.find(2); | |
| 376 EXPECT_TRUE(iter == map.end()); | |
| 377 | |
| 378 // Try to add more to trigger rehashing. | |
| 379 for (int i = 2; i < 32; ++i) { | |
| 380 TheMap::AddResult addResult = map.insert(i, MoveOnly(i * 10)); | |
| 381 EXPECT_TRUE(addResult.isNewEntry); | |
| 382 EXPECT_EQ(i, addResult.storedValue->key); | |
| 383 EXPECT_EQ(i * 10, addResult.storedValue->value.value()); | |
| 384 } | |
| 385 | |
| 386 iter = map.find(1); | |
| 387 ASSERT_TRUE(iter != map.end()); | |
| 388 EXPECT_EQ(1, iter->key); | |
| 389 EXPECT_EQ(10, iter->value.value()); | |
| 390 | |
| 391 iter = map.find(7); | |
| 392 ASSERT_TRUE(iter != map.end()); | |
| 393 EXPECT_EQ(7, iter->key); | |
| 394 EXPECT_EQ(70, iter->value.value()); | |
| 395 | |
| 396 { | |
| 397 TheMap::AddResult addResult = map.set(9, MoveOnly(999)); | |
| 398 EXPECT_FALSE(addResult.isNewEntry); | |
| 399 EXPECT_EQ(9, addResult.storedValue->key); | |
| 400 EXPECT_EQ(999, addResult.storedValue->value.value()); | |
| 401 } | |
| 402 | |
| 403 map.erase(11); | |
| 404 iter = map.find(11); | |
| 405 EXPECT_TRUE(iter == map.end()); | |
| 406 | |
| 407 MoveOnly oneThirty(map.take(13)); | |
| 408 EXPECT_EQ(130, oneThirty.value()); | |
| 409 iter = map.find(13); | |
| 410 EXPECT_TRUE(iter == map.end()); | |
| 411 | |
| 412 map.clear(); | |
| 413 } | |
| 414 | |
| 415 TEST(HashMapTest, MoveOnlyKeyType) { | |
| 416 // The content of this test is similar to the test above, except that the | |
| 417 // types of key and value are swapped. | |
| 418 using TheMap = HashMap<MoveOnly, int>; | |
| 419 TheMap map; | |
| 420 { | |
| 421 TheMap::AddResult addResult = map.insert(MoveOnly(1), 10); | |
| 422 EXPECT_TRUE(addResult.isNewEntry); | |
| 423 EXPECT_EQ(1, addResult.storedValue->key.value()); | |
| 424 EXPECT_EQ(10, addResult.storedValue->value); | |
| 425 } | |
| 426 auto iter = map.find(MoveOnly(1)); | |
| 427 ASSERT_TRUE(iter != map.end()); | |
| 428 EXPECT_EQ(1, iter->key.value()); | |
| 429 EXPECT_EQ(10, iter->value); | |
| 430 | |
| 431 iter = map.find(MoveOnly(2)); | |
| 432 EXPECT_TRUE(iter == map.end()); | |
| 433 | |
| 434 for (int i = 2; i < 32; ++i) { | |
| 435 TheMap::AddResult addResult = map.insert(MoveOnly(i), i * 10); | |
| 436 EXPECT_TRUE(addResult.isNewEntry); | |
| 437 EXPECT_EQ(i, addResult.storedValue->key.value()); | |
| 438 EXPECT_EQ(i * 10, addResult.storedValue->value); | |
| 439 } | |
| 440 | |
| 441 iter = map.find(MoveOnly(1)); | |
| 442 ASSERT_TRUE(iter != map.end()); | |
| 443 EXPECT_EQ(1, iter->key.value()); | |
| 444 EXPECT_EQ(10, iter->value); | |
| 445 | |
| 446 iter = map.find(MoveOnly(7)); | |
| 447 ASSERT_TRUE(iter != map.end()); | |
| 448 EXPECT_EQ(7, iter->key.value()); | |
| 449 EXPECT_EQ(70, iter->value); | |
| 450 | |
| 451 { | |
| 452 TheMap::AddResult addResult = map.set(MoveOnly(9), 999); | |
| 453 EXPECT_FALSE(addResult.isNewEntry); | |
| 454 EXPECT_EQ(9, addResult.storedValue->key.value()); | |
| 455 EXPECT_EQ(999, addResult.storedValue->value); | |
| 456 } | |
| 457 | |
| 458 map.erase(MoveOnly(11)); | |
| 459 iter = map.find(MoveOnly(11)); | |
| 460 EXPECT_TRUE(iter == map.end()); | |
| 461 | |
| 462 int oneThirty = map.take(MoveOnly(13)); | |
| 463 EXPECT_EQ(130, oneThirty); | |
| 464 iter = map.find(MoveOnly(13)); | |
| 465 EXPECT_TRUE(iter == map.end()); | |
| 466 | |
| 467 map.clear(); | |
| 468 } | |
| 469 | |
| 470 class CountCopy final { | |
| 471 public: | |
| 472 CountCopy() : m_counter(nullptr) {} | |
| 473 explicit CountCopy(int& counter) : m_counter(&counter) {} | |
| 474 CountCopy(const CountCopy& other) : m_counter(other.m_counter) { | |
| 475 if (m_counter) | |
| 476 ++*m_counter; | |
| 477 } | |
| 478 CountCopy& operator=(const CountCopy& other) { | |
| 479 m_counter = other.m_counter; | |
| 480 if (m_counter) | |
| 481 ++*m_counter; | |
| 482 return *this; | |
| 483 } | |
| 484 | |
| 485 private: | |
| 486 int* m_counter; | |
| 487 }; | |
| 488 | |
| 489 TEST(HashMapTest, MoveShouldNotMakeCopy) { | |
| 490 HashMap<int, CountCopy> map; | |
| 491 int counter = 0; | |
| 492 map.insert(1, CountCopy(counter)); | |
| 493 | |
| 494 HashMap<int, CountCopy> other(map); | |
| 495 counter = 0; | |
| 496 map = std::move(other); | |
| 497 EXPECT_EQ(0, counter); | |
| 498 | |
| 499 counter = 0; | |
| 500 HashMap<int, CountCopy> yetAnother(std::move(map)); | |
| 501 EXPECT_EQ(0, counter); | |
| 502 } | |
| 503 | |
| 504 TEST(HashMapTest, UniquePtrAsKey) { | |
| 505 using Pointer = std::unique_ptr<int>; | |
| 506 using Map = HashMap<Pointer, int>; | |
| 507 Map map; | |
| 508 int* onePointer = new int(1); | |
| 509 { | |
| 510 Map::AddResult addResult = map.insert(Pointer(onePointer), 1); | |
| 511 EXPECT_TRUE(addResult.isNewEntry); | |
| 512 EXPECT_EQ(onePointer, addResult.storedValue->key.get()); | |
| 513 EXPECT_EQ(1, *addResult.storedValue->key); | |
| 514 EXPECT_EQ(1, addResult.storedValue->value); | |
| 515 } | |
| 516 auto iter = map.find(onePointer); | |
| 517 ASSERT_TRUE(iter != map.end()); | |
| 518 EXPECT_EQ(onePointer, iter->key.get()); | |
| 519 EXPECT_EQ(1, iter->value); | |
| 520 | |
| 521 Pointer nonexistent(new int(42)); | |
| 522 iter = map.find(nonexistent.get()); | |
| 523 EXPECT_TRUE(iter == map.end()); | |
| 524 | |
| 525 // Insert more to cause a rehash. | |
| 526 for (int i = 2; i < 32; ++i) { | |
| 527 Map::AddResult addResult = map.insert(Pointer(new int(i)), i); | |
| 528 EXPECT_TRUE(addResult.isNewEntry); | |
| 529 EXPECT_EQ(i, *addResult.storedValue->key); | |
| 530 EXPECT_EQ(i, addResult.storedValue->value); | |
| 531 } | |
| 532 | |
| 533 iter = map.find(onePointer); | |
| 534 ASSERT_TRUE(iter != map.end()); | |
| 535 EXPECT_EQ(onePointer, iter->key.get()); | |
| 536 EXPECT_EQ(1, iter->value); | |
| 537 | |
| 538 EXPECT_EQ(1, map.take(onePointer)); | |
| 539 // From now on, |onePointer| is a dangling pointer. | |
| 540 | |
| 541 iter = map.find(onePointer); | |
| 542 EXPECT_TRUE(iter == map.end()); | |
| 543 } | |
| 544 | |
| 545 TEST(HashMapTest, UniquePtrAsValue) { | |
| 546 using Pointer = std::unique_ptr<int>; | |
| 547 using Map = HashMap<int, Pointer>; | |
| 548 Map map; | |
| 549 { | |
| 550 Map::AddResult addResult = map.insert(1, Pointer(new int(1))); | |
| 551 EXPECT_TRUE(addResult.isNewEntry); | |
| 552 EXPECT_EQ(1, addResult.storedValue->key); | |
| 553 EXPECT_EQ(1, *addResult.storedValue->value); | |
| 554 } | |
| 555 auto iter = map.find(1); | |
| 556 ASSERT_TRUE(iter != map.end()); | |
| 557 EXPECT_EQ(1, iter->key); | |
| 558 EXPECT_EQ(1, *iter->value); | |
| 559 | |
| 560 int* onePointer = map.at(1); | |
| 561 EXPECT_TRUE(onePointer); | |
| 562 EXPECT_EQ(1, *onePointer); | |
| 563 | |
| 564 iter = map.find(42); | |
| 565 EXPECT_TRUE(iter == map.end()); | |
| 566 | |
| 567 for (int i = 2; i < 32; ++i) { | |
| 568 Map::AddResult addResult = map.insert(i, Pointer(new int(i))); | |
| 569 EXPECT_TRUE(addResult.isNewEntry); | |
| 570 EXPECT_EQ(i, addResult.storedValue->key); | |
| 571 EXPECT_EQ(i, *addResult.storedValue->value); | |
| 572 } | |
| 573 | |
| 574 iter = map.find(1); | |
| 575 ASSERT_TRUE(iter != map.end()); | |
| 576 EXPECT_EQ(1, iter->key); | |
| 577 EXPECT_EQ(1, *iter->value); | |
| 578 | |
| 579 Pointer one(map.take(1)); | |
| 580 ASSERT_TRUE(one); | |
| 581 EXPECT_EQ(1, *one); | |
| 582 | |
| 583 Pointer empty(map.take(42)); | |
| 584 EXPECT_TRUE(!empty); | |
| 585 | |
| 586 iter = map.find(1); | |
| 587 EXPECT_TRUE(iter == map.end()); | |
| 588 | |
| 589 { | |
| 590 Map::AddResult addResult = map.insert(1, std::move(one)); | |
| 591 EXPECT_TRUE(addResult.isNewEntry); | |
| 592 EXPECT_EQ(1, addResult.storedValue->key); | |
| 593 EXPECT_EQ(1, *addResult.storedValue->value); | |
| 594 } | |
| 595 } | |
| 596 | |
| 597 TEST(HashMapTest, MoveOnlyPairKeyType) { | |
| 598 using Pair = std::pair<MoveOnly, int>; | |
| 599 using TheMap = HashMap<Pair, int>; | |
| 600 TheMap map; | |
| 601 { | |
| 602 TheMap::AddResult addResult = map.insert(Pair(MoveOnly(1), -1), 10); | |
| 603 EXPECT_TRUE(addResult.isNewEntry); | |
| 604 EXPECT_EQ(1, addResult.storedValue->key.first.value()); | |
| 605 EXPECT_EQ(-1, addResult.storedValue->key.second); | |
| 606 EXPECT_EQ(10, addResult.storedValue->value); | |
| 607 } | |
| 608 auto iter = map.find(Pair(MoveOnly(1), -1)); | |
| 609 ASSERT_TRUE(iter != map.end()); | |
| 610 EXPECT_EQ(1, iter->key.first.value()); | |
| 611 EXPECT_EQ(-1, iter->key.second); | |
| 612 EXPECT_EQ(10, iter->value); | |
| 613 | |
| 614 iter = map.find(Pair(MoveOnly(1), 0)); | |
| 615 EXPECT_TRUE(iter == map.end()); | |
| 616 | |
| 617 for (int i = 2; i < 32; ++i) { | |
| 618 TheMap::AddResult addResult = map.insert(Pair(MoveOnly(i), -i), i * 10); | |
| 619 EXPECT_TRUE(addResult.isNewEntry); | |
| 620 EXPECT_EQ(i, addResult.storedValue->key.first.value()); | |
| 621 EXPECT_EQ(-i, addResult.storedValue->key.second); | |
| 622 EXPECT_EQ(i * 10, addResult.storedValue->value); | |
| 623 } | |
| 624 | |
| 625 iter = map.find(Pair(MoveOnly(1), -1)); | |
| 626 ASSERT_TRUE(iter != map.end()); | |
| 627 EXPECT_EQ(1, iter->key.first.value()); | |
| 628 EXPECT_EQ(-1, iter->key.second); | |
| 629 EXPECT_EQ(10, iter->value); | |
| 630 | |
| 631 iter = map.find(Pair(MoveOnly(7), -7)); | |
| 632 ASSERT_TRUE(iter != map.end()); | |
| 633 EXPECT_EQ(7, iter->key.first.value()); | |
| 634 EXPECT_EQ(-7, iter->key.second); | |
| 635 EXPECT_EQ(70, iter->value); | |
| 636 | |
| 637 { | |
| 638 TheMap::AddResult addResult = map.set(Pair(MoveOnly(9), -9), 999); | |
| 639 EXPECT_FALSE(addResult.isNewEntry); | |
| 640 EXPECT_EQ(9, addResult.storedValue->key.first.value()); | |
| 641 EXPECT_EQ(-9, addResult.storedValue->key.second); | |
| 642 EXPECT_EQ(999, addResult.storedValue->value); | |
| 643 } | |
| 644 | |
| 645 map.erase(Pair(MoveOnly(11), -11)); | |
| 646 iter = map.find(Pair(MoveOnly(11), -11)); | |
| 647 EXPECT_TRUE(iter == map.end()); | |
| 648 | |
| 649 int oneThirty = map.take(Pair(MoveOnly(13), -13)); | |
| 650 EXPECT_EQ(130, oneThirty); | |
| 651 iter = map.find(Pair(MoveOnly(13), -13)); | |
| 652 EXPECT_TRUE(iter == map.end()); | |
| 653 | |
| 654 map.clear(); | |
| 655 } | |
| 656 | |
| 657 bool isOneTwoThree(const HashMap<int, int>& map) { | |
| 658 return map.size() == 3 && map.contains(1) && map.contains(2) && | |
| 659 map.contains(3) && map.at(1) == 11 && map.at(2) == 22 && | |
| 660 map.at(3) == 33; | |
| 661 }; | |
| 662 | |
| 663 HashMap<int, int> returnOneTwoThree() { | |
| 664 return {{1, 11}, {2, 22}, {3, 33}}; | |
| 665 }; | |
| 666 | |
| 667 TEST(HashMapTest, InitializerList) { | |
| 668 HashMap<int, int> empty({}); | |
| 669 EXPECT_TRUE(empty.isEmpty()); | |
| 670 | |
| 671 HashMap<int, int> one({{1, 11}}); | |
| 672 EXPECT_EQ(one.size(), 1u); | |
| 673 EXPECT_TRUE(one.contains(1)); | |
| 674 EXPECT_EQ(one.at(1), 11); | |
| 675 | |
| 676 HashMap<int, int> oneTwoThree({{1, 11}, {2, 22}, {3, 33}}); | |
| 677 EXPECT_EQ(oneTwoThree.size(), 3u); | |
| 678 EXPECT_TRUE(oneTwoThree.contains(1)); | |
| 679 EXPECT_TRUE(oneTwoThree.contains(2)); | |
| 680 EXPECT_TRUE(oneTwoThree.contains(3)); | |
| 681 EXPECT_EQ(oneTwoThree.at(1), 11); | |
| 682 EXPECT_EQ(oneTwoThree.at(2), 22); | |
| 683 EXPECT_EQ(oneTwoThree.at(3), 33); | |
| 684 | |
| 685 // Put some jank so we can check if the assignments can clear them later. | |
| 686 empty.insert(9999, 99999); | |
| 687 one.insert(9999, 99999); | |
| 688 oneTwoThree.insert(9999, 99999); | |
| 689 | |
| 690 empty = {}; | |
| 691 EXPECT_TRUE(empty.isEmpty()); | |
| 692 | |
| 693 one = {{1, 11}}; | |
| 694 EXPECT_EQ(one.size(), 1u); | |
| 695 EXPECT_TRUE(one.contains(1)); | |
| 696 EXPECT_EQ(one.at(1), 11); | |
| 697 | |
| 698 oneTwoThree = {{1, 11}, {2, 22}, {3, 33}}; | |
| 699 EXPECT_EQ(oneTwoThree.size(), 3u); | |
| 700 EXPECT_TRUE(oneTwoThree.contains(1)); | |
| 701 EXPECT_TRUE(oneTwoThree.contains(2)); | |
| 702 EXPECT_TRUE(oneTwoThree.contains(3)); | |
| 703 EXPECT_EQ(oneTwoThree.at(1), 11); | |
| 704 EXPECT_EQ(oneTwoThree.at(2), 22); | |
| 705 EXPECT_EQ(oneTwoThree.at(3), 33); | |
| 706 | |
| 707 // Other ways of construction: as a function parameter and in a return | |
| 708 // statement. | |
| 709 EXPECT_TRUE(isOneTwoThree({{1, 11}, {2, 22}, {3, 33}})); | |
| 710 EXPECT_TRUE(isOneTwoThree(returnOneTwoThree())); | |
| 711 } | |
| 712 | |
| 713 } // anonymous namespace | |
| 714 | |
| 715 } // namespace WTF | |
| OLD | NEW |