Index: lib/sanitizer_common/sanitizer_addrhashmap.h =================================================================== --- lib/sanitizer_common/sanitizer_addrhashmap.h +++ lib/sanitizer_common/sanitizer_addrhashmap.h @@ -88,8 +88,96 @@ bool create_; }; + template + struct HashValue { + HashValue(uptr a, U v) : addr(a), value(v) {} + uptr addr; + U value; + }; + + class Iterator { + public: + Iterator(AddrHashMap* map) : + map_(map), bucket_idx_(0), embed_idx_(0), add_idx_(0) { + if (operator*().addr == 0) + operator++(); + } + Iterator(const Iterator& src) = default; + Iterator& operator=(const Iterator& src) = default; + ~Iterator() { + if (bucket_idx_ < kSize && embed_idx_ >= kBucketSize) { + Bucket *b = &map_->table_[bucket_idx_]; + b->mtx.ReadUnlock(); + } + } + HashValue operator*() { + Bucket *b = &map_->table_[bucket_idx_]; + Cell *c; + if (embed_idx_ < kBucketSize) { + c = &b->cells[embed_idx_]; + } else { + AddBucket *add = (AddBucket*)atomic_load(&b->add, memory_order_relaxed); + if (add == nullptr) + return HashValue(0, nullptr); + c = &add->cells[add_idx_]; + } + uptr addr = atomic_load(&c->addr, memory_order_relaxed); + return HashValue(addr, &c->val); + } + Iterator& operator++() { + do { + if (bucket_idx_ == kSize) + return *this; + if (embed_idx_ < kBucketSize) { + ++embed_idx_; + } else { + Bucket *b = &map_->table_[bucket_idx_]; + // We must hold the read lock across the iteration. + b->mtx.ReadLock(); + AddBucket *add = (AddBucket*)atomic_load(&b->add, + memory_order_relaxed); + if (add == nullptr || ++add_idx_ >= add->size) { + b->mtx.ReadUnlock(); + add_idx_ = 0; + embed_idx_ = 0; + ++bucket_idx_; + } + } + } while (operator*().addr == 0); + return *this; + } + Iterator operator++(int) { + Iterator temp(*this); + operator++(); + return temp; + } + // Decrement not supported. + bool operator==(const Iterator& vs) const { + return (vs.map_ == map_ && vs.bucket_idx_ == bucket_idx_ && + vs.embed_idx_ == embed_idx_ && vs.add_idx_ == add_idx_); + } + bool operator!=(const Iterator& vs) const { + return (vs.map_ != map_ || vs.bucket_idx_ != bucket_idx_ || + vs.embed_idx_ != embed_idx_ || vs.add_idx_ != add_idx_); + } + + private: + Iterator(AddrHashMap* map, uptr bucket_idx) : + map_(map), bucket_idx_(bucket_idx), embed_idx_(0), add_idx_(0) {} + friend AddrHashMap; + AddrHashMap *map_; + uptr bucket_idx_; + uptr embed_idx_; + uptr add_idx_; + }; + + // We do not support erase or insert iterators. + Iterator begin() { return Iterator(this); } + Iterator end() { return Iterator(this, kSize); } + private: friend class Handle; + friend class Iterator; Bucket *table_; void acquire(Handle *h); Index: lib/sanitizer_common/tests/CMakeLists.txt =================================================================== --- lib/sanitizer_common/tests/CMakeLists.txt +++ lib/sanitizer_common/tests/CMakeLists.txt @@ -9,6 +9,7 @@ endif() set(SANITIZER_UNITTESTS + sanitizer_addrhashmap_test.cc sanitizer_allocator_test.cc sanitizer_atomic_test.cc sanitizer_bitvector_test.cc Index: lib/sanitizer_common/tests/sanitizer_addrhashmap_test.cc =================================================================== --- /dev/null +++ lib/sanitizer_common/tests/sanitizer_addrhashmap_test.cc @@ -0,0 +1,61 @@ +//===-- sanitizer_bitvector_test.cc ---------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file is a part of Sanitizer runtime. +// Tests for sanitizer_bitvector.h. +// +//===----------------------------------------------------------------------===// + +#include "sanitizer_common/sanitizer_addrhashmap.h" +#include "sanitizer_test_utils.h" +#include "gtest/gtest.h" + +using namespace __sanitizer; + +typedef AddrHashMap IntHashMap; + +static void addNewEntry(IntHashMap *IntMap, uptr Addr, int Value) { + IntHashMap::Handle H(IntMap, Addr, false/*!remove*/, true/*create*/); + EXPECT_TRUE(H.exists()); + EXPECT_TRUE(H.created()); + // Sort of awkward: works better with a struct. + *(H.operator->()) = Value; +} + +TEST(SanitizerCommon, AddrHashMap) { + IntHashMap *IntMap; + static char HashMem[sizeof(*IntMap)]; + IntMap = new((void *)&HashMem) IntHashMap(); + + uptr TestAddr = 0x1234; + addNewEntry(IntMap, TestAddr, 42); + // Handles need scope exit to work properly: + { + IntHashMap::Handle Lookup(IntMap, TestAddr, false/*!remove*/, + false/*!create*/); + EXPECT_TRUE(Lookup.exists()); + EXPECT_FALSE(Lookup.created()); + } + { + IntHashMap::Handle Rm(IntMap, TestAddr, true/*remove*/); + EXPECT_TRUE(Rm.exists()); + } + addNewEntry(IntMap, TestAddr, 42); + addNewEntry(IntMap, TestAddr+1, 43); + addNewEntry(IntMap, TestAddr+2, 44); + + int Count = 0; + for (auto Iter = IntMap->begin(); Iter != IntMap->end(); ++Iter, ++Count) { + uptr Addr = (*Iter).addr; + int *Value = (*Iter).value; + EXPECT_TRUE(Addr >= TestAddr && Addr <= TestAddr+2); + EXPECT_TRUE(*Value >= 42 && *Value <= 44); + } + EXPECT_EQ(Count, 3); +}