#include "storm/storage/BitVectorHashMap.h" #include #include #include #include "storm/utility/macros.h" #include "storm/exceptions/InternalException.h" namespace storm { namespace storage { template BitVectorHashMap::BitVectorHashMapIterator::BitVectorHashMapIterator(BitVectorHashMap const& map, BitVector::const_iterator indexIt) : map(map), indexIt(indexIt) { // Intentionally left empty. } template bool BitVectorHashMap::BitVectorHashMapIterator::operator==(BitVectorHashMapIterator const& other) { return &map == &other.map && *indexIt == *other.indexIt; } template bool BitVectorHashMap::BitVectorHashMapIterator::operator!=(BitVectorHashMapIterator const& other) { return !(*this == other); } template typename BitVectorHashMap::BitVectorHashMapIterator& BitVectorHashMap::BitVectorHashMapIterator::operator++(int) { ++indexIt; return *this; } template typename BitVectorHashMap::BitVectorHashMapIterator& BitVectorHashMap::BitVectorHashMapIterator::operator++() { ++indexIt; return *this; } template std::pair BitVectorHashMap::BitVectorHashMapIterator::operator*() const { return map.getBucketAndValue(*indexIt); } template BitVectorHashMap::BitVectorHashMap(uint64_t bucketSize, uint64_t initialSize, double loadFactor) : loadFactor(loadFactor), bucketSize(bucketSize), currentSize(1), numberOfElements(0) { STORM_LOG_ASSERT(bucketSize % 64 == 0, "Bucket size must be a multiple of 64."); while (initialSize > 0) { ++currentSize; initialSize >>= 1; } // Create the underlying containers. buckets = storm::storage::BitVector(bucketSize * (1ull << currentSize)); occupied = storm::storage::BitVector(1ull << currentSize); values = std::vector(1ull << currentSize); } template bool BitVectorHashMap::isBucketOccupied(uint_fast64_t bucket) const { return occupied.get(bucket); } template uint64_t BitVectorHashMap::size() const { return numberOfElements; } template uint64_t BitVectorHashMap::capacity() const { return 1ull << currentSize; } template void BitVectorHashMap::increaseSize() { ++currentSize; STORM_LOG_TRACE("Increasing size of hash map from " << (1ull << (currentSize - 1)) << " to " << (1ull << currentSize) << "."); // Create new containers and swap them with the old ones. storm::storage::BitVector oldBuckets(bucketSize * (1ull << currentSize)); std::swap(oldBuckets, buckets); storm::storage::BitVector oldOccupied = storm::storage::BitVector(1ull << currentSize); std::swap(oldOccupied, occupied); std::vector oldValues = std::vector(1ull << currentSize); std::swap(oldValues, values); // Now iterate through the elements and reinsert them in the new storage. uint64_t oldSize = numberOfElements; numberOfElements = 0; for (auto bucketIndex : oldOccupied) { findOrAddAndGetBucket(oldBuckets.get(bucketIndex * bucketSize, bucketSize), oldValues[bucketIndex]); } STORM_LOG_ASSERT(oldSize == numberOfElements, "Size mismatch in rehashing. Size before was " << oldSize << " and new size is " << numberOfElements << "."); } 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) { checkIncreaseSize(); std::pair flagAndBucket = this->findBucketToInsert(key); if (flagAndBucket.first) { return std::make_pair(values[flagAndBucket.second], flagAndBucket.second); } else { // Insert the new bits into the bucket. buckets.set(flagAndBucket.second * bucketSize, key); occupied.set(flagAndBucket.second); values[flagAndBucket.second] = value; ++numberOfElements; return std::make_pair(value, flagAndBucket.second); } } template bool BitVectorHashMap::checkIncreaseSize() { // If the load of the map is too high, we increase the size. if (numberOfElements >= loadFactor * (1ull << currentSize)) { this->increaseSize(); return true; } return false; } template ValueType BitVectorHashMap::getValue(storm::storage::BitVector const& key) const { std::pair flagBucketPair = this->findBucket(key); STORM_LOG_ASSERT(flagBucketPair.first, "Unknown key."); return values[flagBucketPair.second]; } template ValueType BitVectorHashMap::getValue(uint64_t bucket) const { return values[bucket]; } template bool BitVectorHashMap::contains(storm::storage::BitVector const& key) const { return findBucket(key).first; } template typename BitVectorHashMap::const_iterator BitVectorHashMap::begin() const { return const_iterator(*this, occupied.begin()); } template typename BitVectorHashMap::const_iterator BitVectorHashMap::end() const { return const_iterator(*this, occupied.end()); } template uint64_t BitVectorHashMap::getCurrentShiftWidth() const { return (sizeof(decltype(hasher(storm::storage::BitVector()))) * 8 - currentSize); } template std::pair BitVectorHashMap::findBucket(storm::storage::BitVector const& key) const { uint64_t bucket = hasher(key) >> this->getCurrentShiftWidth(); while (isBucketOccupied(bucket)) { if (buckets.matches(bucket * bucketSize, key)) { return std::make_pair(true, bucket); } ++bucket; if (bucket == (1ull << currentSize)) { bucket = 0; } } return std::make_pair(false, bucket); } template std::pair BitVectorHashMap::findBucketToInsert(storm::storage::BitVector const& key) { uint64_t bucket = hasher(key) >> this->getCurrentShiftWidth(); while (isBucketOccupied(bucket)) { if (buckets.matches(bucket * bucketSize, key)) { return std::make_pair(true, bucket); } ++bucket; if (bucket == (1ull << currentSize)) { bucket = 0; } } return std::make_pair(false, bucket); } template std::pair BitVectorHashMap::getBucketAndValue(uint64_t bucket) const { return std::make_pair(buckets.get(bucket * bucketSize, bucketSize), values[bucket]); } template void BitVectorHashMap::remap(std::function const& remapping) { for (auto pos : occupied) { values[pos] = remapping(values[pos]); } } template class BitVectorHashMap; template class BitVectorHashMap; } }