// OpenSTA, Static Timing Analyzer // Copyright (c) 2020, Parallax Software, Inc. // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . #ifndef STA_HASHMAP_H #define STA_HASHMAP_H #include // size_t #include "Hash.hh" namespace sta { template class HashMapBucket; template class PtrHash { public: Hash operator()(const OBJECT obj) const { return hashPtr(obj); } }; template class PtrEqual { public: bool operator()(const OBJECT obj1, const OBJECT obj2) const { return obj1 == obj2; } }; template , class EQUAL = PtrEqual > class HashMap { public: HashMap(); explicit HashMap(size_t capacity, bool auto_resize); explicit HashMap(size_t capacity, bool auto_resize, HASH hash, EQUAL equal); ~HashMap(); // Number of objects in the table. size_t size() const { return size_; } // Number of hash buckets. size_t capacity() const { return capacity_; } // Multi-threaded findKey is safe during resize. void resize(size_t capacity); void insert(KEY key, VALUE value); VALUE findKey(KEY key); VALUE findKey(KEY key) const; void findKey(KEY key, VALUE &value, bool &exists) const; void findKey(KEY key, KEY &map_key, VALUE &value, bool &exists) const; bool hasKey(KEY key) const; void eraseKey(KEY key); bool empty() const; void clear(); void deleteContentsClear(); void deleteArrayContentsClear(); int longestBucketLength() const; Hash longestBucketHash() const; int bucketLength(Hash hash) const; void deleteContents() { Iterator iter(this); while (iter.hasNext()) delete iter.next(); } void deleteArrayContents() { Iterator iter(this); while (iter.hasNext()) delete [] iter.next(); } // Java style container itererator // Map::Iterator iter(map); // while (iter.hasNext()) { // Value *v = iter.next(); // } class Iterator { public: Iterator() : container_(NULL) {} explicit Iterator(HashMap *container) { init(container); } explicit Iterator(HashMap &container) { init(container); } void init(HashMap *container) { container_ = container; hash_= 0; next_ = NULL; if (container_) findNext(); } void init(HashMap &container) { container_ = &container; hash_= 0; next_ = NULL; if (container_) findNext(); } bool hasNext() { return container_ && next_ != NULL; } VALUE next() { HashMapBucket *next = next_; findNext(); return next->value(); } void next(KEY &key, VALUE &value) { HashMapBucket *next = next_; findNext(); key = next->key(); value = next->value(); } HashMap *container() { return container_; } private: void findNext() { if (next_) next_ = next_->next(); while (next_ == NULL && hash_ < container_->capacity()) next_ = container_->table_[hash_++]; } HashMap *container_; size_t hash_; HashMapBucket *next_; }; class ConstIterator { public: ConstIterator() : container_(NULL) {} explicit ConstIterator(const HashMap *container) { init(container); } explicit ConstIterator(const HashMap &container) { init(container); } void init(const HashMap *container) { container_ = container; hash_= 0; next_ = NULL; findNext(); } void init(HashMap &container) { container_ = &container; hash_= 0; next_ = NULL; findNext(); } bool hasNext() { return container_ && next_ != NULL; } VALUE next() { HashMapBucket *next = next_; findNext(); return next->value(); } void next(// Return values. KEY &key, VALUE &value) { HashMapBucket *next = next_; findNext(); key = next->key(); value = next->value(); } HashMap *container() { return container_; } private: void findNext() { if (next_) next_ = next_->next(); while (next_ == NULL && hash_ < container_->capacity()) next_ = container_->table_[hash_++]; } const HashMap *container_; size_t hash_; HashMapBucket *next_; }; protected: void initTable(); static const int default_capacity = (2 << 6) - 1; // Table size. size_t capacity_; bool auto_resize_; HASH hash_; EQUAL equal_; size_t size_; HashMapBucket **table_; HashMap *tmp_; }; template class HashMapBucket { public: HashMapBucket(KEY key, VALUE value, HashMapBucket *next); KEY key() const { return key_; } VALUE &value() { return value_; } void set(KEY key, VALUE value) { key_ = key; value_ = value; } HashMapBucket *next() const { return next_; } void setNext(HashMapBucket *next) { next_ = next; } private: KEY key_; VALUE value_; HashMapBucket *next_; }; template HashMapBucket::HashMapBucket(KEY key, VALUE value, HashMapBucket *next) : key_(key), value_(value), next_(next) { } //////////////////////////////////////////////////////////////// template HashMap::HashMap() : capacity_(default_capacity), auto_resize_(true), hash_(HASH()), equal_(EQUAL()) { initTable(); } template HashMap::HashMap(size_t capacity, bool auto_resize) : auto_resize_(auto_resize), capacity_(capacity), hash_(HASH()), equal_(EQUAL()) { initTable(); } template HashMap::HashMap(size_t capacity, bool auto_resize, HASH hash, EQUAL equal) : capacity_(capacity), auto_resize_(auto_resize), hash_(hash), equal_(equal) { initTable(); } template void HashMap::initTable() { size_ = 0; tmp_ = NULL; table_ = new HashMapBucket*[capacity_]; for (size_t i = 0; i < capacity_; i++) table_[i] = NULL; } template HashMap::~HashMap() { for (size_t hash = 0; hash < capacity_; hash++) { HashMapBucket *next; for (HashMapBucket *bucket = table_[hash]; bucket; bucket = next) { next = bucket->next(); delete bucket; } } delete [] table_; delete tmp_; } template bool HashMap::hasKey(KEY key) const { size_t hash = hash_(key) % capacity_; HashMapBucket *head = table_[hash]; for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { if (equal_(bucket->key(), key)) { return true; } } return false; } template VALUE HashMap::findKey(KEY key) { size_t hash = hash_(key) % capacity_; HashMapBucket *head = table_[hash]; for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { KEY bucket_key = bucket->key(); if (equal_(bucket_key, key)) { return bucket->value(); } } return NULL; } template VALUE HashMap::findKey(KEY key) const { size_t hash = hash_(key) % capacity_; HashMapBucket *head = table_[hash]; for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { KEY bucket_key = bucket->key(); if (equal_(bucket_key, key)) { return bucket->value(); } } return NULL; } template void HashMap::findKey(KEY key, VALUE &value, bool &exists) const { size_t hash = hash_(key) % capacity_; HashMapBucket *head = table_[hash]; for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { KEY bucket_key = bucket->key(); if (equal_(bucket_key, key)) { value = bucket->value(); exists = true; return; } } exists = false; } template void HashMap::findKey(KEY key, KEY &map_key, VALUE &value, bool &exists) const { size_t hash = hash_(key) % capacity_; HashMapBucket *head = table_[hash]; for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { KEY bucket_key = bucket->key(); if (equal_(bucket_key, key)) { map_key = bucket->key(); value = bucket->value(); exists = true; return; } } exists = false; } template void HashMap::insert(KEY key, VALUE value) { size_t hash = hash_(key) % capacity_; HashMapBucket *head = table_[hash]; for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { if (equal_(bucket->key(), key)) { bucket->set(key, value); return; } } HashMapBucket *bucket = new HashMapBucket(key, value, head); table_[hash] = bucket; size_++; if (size_ > capacity_ && auto_resize_) resize(nextMersenne(capacity_)); } template void HashMap::resize(size_t capacity) { if (capacity != capacity_) { if (size_ == 0) { // Table is empty. capacity_ = capacity; delete [] table_; delete tmp_; initTable(); } else { delete tmp_; // Copy entries to tmp. tmp_ = new HashMap(capacity, auto_resize_, hash_, equal_); for (size_t hash = 0; hash < capacity_; hash++) { for (HashMapBucket *bucket = table_[hash]; bucket; bucket = bucket->next()) tmp_->insert(bucket->key(), bucket->value()); } size_t prev_capacity = capacity_; HashMapBucket **prev_table = table_; // Switch over. table_ = tmp_->table_; capacity_ = capacity; tmp_->capacity_ = prev_capacity; tmp_->table_ = prev_table; } } } template void HashMap::eraseKey(KEY key) { size_t hash = hash_(key) % capacity_; HashMapBucket *head = table_[hash]; HashMapBucket *prev = NULL; for (HashMapBucket *bucket = head; bucket; bucket = bucket->next()) { if (equal_(bucket->key(), key)) { if (prev) prev->setNext(bucket->next()); else table_[hash] = bucket->next(); delete bucket; size_--; break; } prev = bucket; } } template void HashMap::deleteContentsClear() { if (size_ > 0) { for (size_t hash = 0; hash < capacity_; hash++) { HashMapBucket *next; for (HashMapBucket *bucket = table_[hash]; bucket; bucket = next) { delete bucket->value(); next = bucket->next(); delete bucket; } table_[hash] = NULL; } size_ = 0; } } template void HashMap::deleteArrayContentsClear() { if (size_ > 0) { for (size_t hash = 0; hash < capacity_; hash++) { HashMapBucket *next; for (HashMapBucket *bucket = table_[hash]; bucket; bucket = next) { delete [] bucket->value(); next = bucket->next(); delete bucket; } table_[hash] = NULL; } size_ = 0; } } template void HashMap::clear() { if (size_ > 0) { for (size_t hash = 0; hash < capacity_; hash++) { HashMapBucket *next; for (HashMapBucket *bucket = table_[hash]; bucket; bucket = next) { next = bucket->next(); delete bucket; } table_[hash] = NULL; } size_ = 0; } } template bool HashMap::empty() const { return size_ == 0; } template int HashMap::longestBucketLength() const { return bucketLength(longestBucketHash()); } template Hash HashMap::longestBucketHash() const { int longest = 0; Hash longest_hash = 0; for (size_t hash = 0; hash < capacity_; hash++) { int length = bucketLength(hash); if (length > longest) { longest = length; longest_hash = hash; } } return longest_hash; } template int HashMap::bucketLength(Hash hash) const { int length = 0; for (HashMapBucket *bucket = table_[hash]; bucket; bucket = bucket->next()) length++; return length; } } // namespace #endif