// IWYU pragma: private, include "WAVM/Inline/HashMap.h" // You should only include this file indirectly by including HashMap.h. // Use these macros to compress the boilerplate template declarations in a non-inline member // function definition for HashMap. #define HASHMAP_PARAMETERS typename Key, typename Value, typename KeyHashPolicy #define HASHMAP_ARGUMENTS Key, Value, KeyHashPolicy template HashMap::HashMap(Uptr reserveNumPairs) : table(reserveNumPairs) { } template HashMap::HashMap(const std::initializer_list& initializerList) : table(initializerList.size()) { for(const Pair& pair : initializerList) { const bool result = add(pair.key, pair.value); WAVM_ASSERT(result); } } template template Value& HashMap::getOrAdd(const Key& key, ValueArgs&&... valueArgs) { const Uptr hashAndOccupancy = KeyHashPolicy::getKeyHash(key) | HashTableBucket::isOccupiedMask; HashTableBucket& bucket = table.getBucketForAdd(hashAndOccupancy, key); if(!bucket.hashAndOccupancy) { bucket.hashAndOccupancy = hashAndOccupancy; bucket.storage.construct(key, std::forward(valueArgs)...); } return bucket.storage.get().value; } template template bool HashMap::add(const Key& key, ValueArgs&&... valueArgs) { const Uptr hashAndOccupancy = KeyHashPolicy::getKeyHash(key) | HashTableBucket::isOccupiedMask; HashTableBucket& bucket = table.getBucketForAdd(hashAndOccupancy, key); if(!bucket.hashAndOccupancy) { bucket.hashAndOccupancy = hashAndOccupancy; bucket.storage.construct(key, std::forward(valueArgs)...); return true; } else { return false; } } template template void HashMap::addOrFail(const Key& key, ValueArgs&&... valueArgs) { const Uptr hashAndOccupancy = KeyHashPolicy::getKeyHash(key) | HashTableBucket::isOccupiedMask; HashTableBucket& bucket = table.getBucketForAdd(hashAndOccupancy, key); WAVM_ASSERT(!bucket.hashAndOccupancy); bucket.hashAndOccupancy = hashAndOccupancy; bucket.storage.construct(key, std::forward(valueArgs)...); } template template Value& HashMap::set(const Key& key, ValueArgs&&... valueArgs) { const Uptr hashAndOccupancy = KeyHashPolicy::getKeyHash(key) | HashTableBucket::isOccupiedMask; HashTableBucket& bucket = table.getBucketForAdd(hashAndOccupancy, key); if(!bucket.hashAndOccupancy) { bucket.hashAndOccupancy = hashAndOccupancy; bucket.storage.construct(key, std::forward(valueArgs)...); } else { bucket.storage.get().value = Value(std::forward(valueArgs)...); } return bucket.storage.get().value; } template bool HashMap::remove(const Key& key) { return table.remove(KeyHashPolicy::getKeyHash(key), key); } template void HashMap::removeOrFail(const Key& key) { const bool removed = table.remove(KeyHashPolicy::getKeyHash(key), key); WAVM_ASSERT(removed); } template bool HashMap::contains(const Key& key) const { const Uptr hash = KeyHashPolicy::getKeyHash(key); const HashTableBucket* bucket = table.getBucketForRead(hash, key); WAVM_ASSERT(!bucket || bucket->hashAndOccupancy == (hash | HashTableBucket::isOccupiedMask)); return bucket != nullptr; } template const Value& HashMap::operator[](const Key& key) const { const Uptr hash = KeyHashPolicy::getKeyHash(key); const HashTableBucket* bucket = table.getBucketForRead(hash, key); WAVM_ASSERT(bucket); if(!bucket) { // In addition to the assert, use WAVM_UNREACHABLE to ensure that GCC's -Wnull-dereference // warning isn't triggered because it thinks this function might return nullptr. WAVM_UNREACHABLE(); } WAVM_ASSERT(bucket->hashAndOccupancy == (hash | HashTableBucket::isOccupiedMask)); return bucket->storage.get().value; } template Value& HashMap::operator[](const Key& key) { const Uptr hash = KeyHashPolicy::getKeyHash(key); HashTableBucket* bucket = table.getBucketForModify(hash, key); WAVM_ASSERT(bucket); if(!bucket) { // In addition to the assert, use WAVM_UNREACHABLE to ensure that GCC's -Wnull-dereference // warning isn't triggered because it thinks this function might return nullptr. WAVM_UNREACHABLE(); } WAVM_ASSERT(bucket->hashAndOccupancy == (hash | HashTableBucket::isOccupiedMask)); return bucket->storage.get().value; } template const Value* HashMap::get(const Key& key) const { const Uptr hash = KeyHashPolicy::getKeyHash(key); const HashTableBucket* bucket = table.getBucketForRead(hash, key); if(!bucket) { return nullptr; } else { WAVM_ASSERT(bucket->hashAndOccupancy == (hash | HashTableBucket::isOccupiedMask)); return &bucket->storage.get().value; } } template Value* HashMap::get(const Key& key) { const Uptr hash = KeyHashPolicy::getKeyHash(key); HashTableBucket* bucket = table.getBucketForModify(hash, key); if(!bucket) { return nullptr; } else { WAVM_ASSERT(bucket->hashAndOccupancy == (hash | HashTableBucket::isOccupiedMask)); return &bucket->storage.get().value; } } template const HashMapPair* HashMap::getPair(const Key& key) const { const Uptr hash = KeyHashPolicy::getKeyHash(key); const HashTableBucket* bucket = table.getBucketForRead(hash, key); if(!bucket) { return nullptr; } else { WAVM_ASSERT(bucket->hashAndOccupancy == (hash | HashTableBucket::isOccupiedMask)); return &bucket->storage.get(); } } template void HashMap::clear() { table.clear(); } template HashMapIterator HashMap::begin() const { // Find the first occupied bucket. HashTableBucket* beginBucket = table.getBuckets(); HashTableBucket* endBucket = table.getBuckets() + table.numBuckets(); while(beginBucket < endBucket && !beginBucket->hashAndOccupancy) { ++beginBucket; }; return HashMapIterator(beginBucket, endBucket); } template HashMapIterator HashMap::end() const { return HashMapIterator(table.getBuckets() + table.numBuckets(), table.getBuckets() + table.numBuckets()); } template Uptr HashMap::size() const { return table.size(); } template void HashMap::analyzeSpaceUsage(Uptr& outTotalMemoryBytes, Uptr& outMaxProbeCount, F32& outOccupancy, F32& outAverageProbeCount) const { return table.analyzeSpaceUsage( outTotalMemoryBytes, outMaxProbeCount, outOccupancy, outAverageProbeCount); } template template HashMapPair::HashMapPair(const Key& inKey, ValueArgs&&... valueArgs) : key(inKey), value(std::forward(valueArgs)...) { } template template HashMapPair::HashMapPair(Key&& inKey, ValueArgs&&... valueArgs) : key(std::move(inKey)), value(std::forward(valueArgs)...) { } template bool HashMapIterator::operator!=(const HashMapIterator& other) { return bucket != other.bucket; } template bool HashMapIterator::operator==(const HashMapIterator& other) { return bucket == other.bucket; } template HashMapIterator::operator bool() const { return bucket < endBucket && bucket->hashAndOccupancy; } template void HashMapIterator::operator++() { do { ++bucket; } while(bucket < endBucket && !bucket->hashAndOccupancy); } template const HashMapPair& HashMapIterator::operator*() const { WAVM_ASSERT(bucket->hashAndOccupancy); return bucket->storage.get(); } template const HashMapPair* HashMapIterator::operator->() const { WAVM_ASSERT(bucket->hashAndOccupancy); return &bucket->storage.get(); } template HashMapIterator::HashMapIterator( const HashTableBucket>* inBucket, const HashTableBucket>* inEndBucket) : bucket(inBucket), endBucket(inEndBucket) { } #undef HASHMAP_PARAMETERS #undef HASHMAP_ARGUMENTS