// 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