#include "src/storage/BitVectorHashMap.h" #include #include "src/utility/macros.h" namespace storm { namespace storage { template const std::vector BitVectorHashMap::sizes = {5, 13, 31, 79, 163, 277, 499, 1021, 2029, 3989, 8059, 16001, 32099, 64301, 127921, 256499, 511111, 1024901, 2048003, 4096891, 8192411, 15485863, 32142191}; template BitVectorHashMap::BitVectorHashMap(uint64_t bucketSize, uint64_t initialSize, double loadFactor) : loadFactor(loadFactor), bucketSize(bucketSize), numberOfElements(0) { STORM_LOG_ASSERT(bucketSize % 64 == 0, "Bucket size must be a multiple of 64."); currentSizeIterator = std::find_if(sizes.begin(), sizes.end(), [=] (uint64_t value) { return value > initialSize; } ); // Create the underlying containers. buckets = storm::storage::BitVector(bucketSize * *currentSizeIterator); occupied = storm::storage::BitVector(*currentSizeIterator); values = std::vector(*currentSizeIterator); } template bool BitVectorHashMap::isBucketOccupied(uint_fast64_t bucket) const { return occupied.get(bucket); } template std::size_t BitVectorHashMap::size() const { return numberOfElements; } template std::size_t BitVectorHashMap::capacity() const { return *currentSizeIterator; } template void BitVectorHashMap::increaseSize() { ++currentSizeIterator; STORM_LOG_ASSERT(currentSizeIterator != sizes.end(), "Hash map became to big."); // Create new containers and swap them with the old ones. numberOfElements = 0; storm::storage::BitVector oldBuckets(bucketSize * *currentSizeIterator); std::swap(oldBuckets, buckets); storm::storage::BitVector oldOccupied = storm::storage::BitVector(*currentSizeIterator); std::swap(oldOccupied, occupied); std::vector oldValues = std::vector(*currentSizeIterator); std::swap(oldValues, values); // Now iterate through the elements and reinsert them in the new storage. bool fail = false; for (auto bucketIndex : oldOccupied) { fail = !this->insertWithoutIncreasingSize(oldBuckets.get(bucketIndex * bucketSize, bucketSize), oldValues[bucketIndex]); // If we failed to insert just one element, we have to redo the procedure with a larger container size. if (fail) { break; } } uint_fast64_t failCount = 0; while (fail) { ++failCount; STORM_LOG_ASSERT(failCount < 2, "Increasing size failed too often."); ++currentSizeIterator; STORM_LOG_ASSERT(currentSizeIterator != sizes.end(), "Hash map became to big."); numberOfElements = 0; buckets = storm::storage::BitVector(bucketSize * *currentSizeIterator); occupied = storm::storage::BitVector(*currentSizeIterator); values = std::vector(*currentSizeIterator); for (auto bucketIndex : oldOccupied) { fail = !this->insertWithoutIncreasingSize(oldBuckets.get(bucketIndex * bucketSize, bucketSize), oldValues[bucketIndex]); // If we failed to insert just one element, we have to redo the procedure with a larger container size. if (fail) { continue; } } } } template bool BitVectorHashMap::insertWithoutIncreasingSize(storm::storage::BitVector const& key, ValueType const& value) { std::tuple flagBucketTuple = this->findBucketToInsert(key); if (std::get<2>(flagBucketTuple)) { return false; } if (std::get<0>(flagBucketTuple)) { return true; } else { // Insert the new bits into the bucket. buckets.set(std::get<1>(flagBucketTuple) * bucketSize, key); occupied.set(std::get<1>(flagBucketTuple)); values[std::get<1>(flagBucketTuple)] = value; ++numberOfElements; return true; } } template ValueType BitVectorHashMap::findOrAdd(storm::storage::BitVector const& key, ValueType const& value) { return findOrAddAndGetBucket(key, value).first; } template std::pair BitVectorHashMap::findOrAddAndGetBucket(storm::storage::BitVector const& key, ValueType const& value) { // If the load of the map is too high, we increase the size. if (numberOfElements >= loadFactor * *currentSizeIterator) { this->increaseSize(); } std::tuple flagBucketTuple = this->findBucketToInsert(key); STORM_LOG_ASSERT(!std::get<2>(flagBucketTuple), "Failed to find bucket for insertion."); if (std::get<0>(flagBucketTuple)) { return std::make_pair(values[std::get<1>(flagBucketTuple)], std::get<1>(flagBucketTuple)); } else { // Insert the new bits into the bucket. buckets.set(std::get<1>(flagBucketTuple) * bucketSize, key); occupied.set(std::get<1>(flagBucketTuple)); values[std::get<1>(flagBucketTuple)] = value; ++numberOfElements; return std::make_pair(value, std::get<1>(flagBucketTuple)); } } template ValueType BitVectorHashMap::getValue(storm::storage::BitVector const& key) const { std::pair flagBucketPair = this->findBucket(key); STORM_LOG_ASSERT(flagBucketPair.first, "Unknown key."); return flagBucketPair.second; } template std::pair BitVectorHashMap::findBucket(storm::storage::BitVector const& key) const { uint_fast64_t initialHash = hasher1(key) % *currentSizeIterator; uint_fast64_t bucket = initialHash; uint_fast64_t counter = 0; while (isBucketOccupied(bucket)) { ++counter; if (buckets.matches(bucket * bucketSize, key)) { return std::make_pair(true, bucket); } bucket += hasher2(key); bucket %= *currentSizeIterator; if (bucket == initialHash) { return std::make_pair(false, bucket); } } return std::make_pair(false, bucket); } template template std::tuple BitVectorHashMap::findBucketToInsert(storm::storage::BitVector const& key) { uint_fast64_t initialHash = hasher1(key) % *currentSizeIterator; uint_fast64_t bucket = initialHash; uint_fast64_t counter = 0; while (isBucketOccupied(bucket)) { ++counter; if (buckets.matches(bucket * bucketSize, key)) { return std::make_tuple(true, bucket, false); } bucket += hasher2(key); bucket %= *currentSizeIterator; if (bucket == initialHash) { if (increaseStorage) { this->increaseSize(); bucket = initialHash = hasher1(key) % *currentSizeIterator; } else { return std::make_tuple(false, bucket, true); } } } return std::make_tuple(false, bucket, false); } template std::pair BitVectorHashMap::getBucketAndValue(std::size_t bucket) const { return std::make_pair(buckets.get(bucket * bucketSize, bucketSize), values[bucket]); } template class BitVectorHashMap; template class BitVectorHashMap; } }