// util/hash-list-inl.h // Copyright 2009-2011 Microsoft Corporation // 2013 Johns Hopkins University (author: Daniel Povey) // See ../../COPYING for clarification regarding multiple authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE, // MERCHANTABLITY OR NON-INFRINGEMENT. // See the Apache 2 License for the specific language governing permissions and // limitations under the License. #ifndef KALDI_UTIL_HASH_LIST_INL_H_ #define KALDI_UTIL_HASH_LIST_INL_H_ // Do not include this file directly. It is included by fast-hash.h namespace kaldi { template HashList::HashList() { list_head_ = NULL; bucket_list_tail_ = static_cast(-1); // invalid. hash_size_ = 0; freed_head_ = NULL; } template void HashList::SetSize(size_t size) { hash_size_ = size; KALDI_ASSERT(list_head_ == NULL && bucket_list_tail_ == static_cast(-1)); // make sure empty. if (size > buckets_.size()) buckets_.resize(size, HashBucket(0, NULL)); } template typename HashList::Elem* HashList::Clear() { // Clears the hashtable and gives ownership of the currently contained list // to the user. for (size_t cur_bucket = bucket_list_tail_; cur_bucket != static_cast(-1); cur_bucket = buckets_[cur_bucket].prev_bucket) { buckets_[cur_bucket].last_elem = NULL; // this is how we indicate "empty". } bucket_list_tail_ = static_cast(-1); Elem *ans = list_head_; list_head_ = NULL; return ans; } template const typename HashList::Elem* HashList::GetList() const { return list_head_; } template inline void HashList::Delete(Elem *e) { e->tail = freed_head_; freed_head_ = e; } template inline typename HashList::Elem* HashList::Find(I key) { size_t index = (static_cast(key) % hash_size_); HashBucket &bucket = buckets_[index]; if (bucket.last_elem == NULL) { return NULL; // empty bucket. } else { Elem *head = (bucket.prev_bucket == static_cast(-1) ? list_head_ : buckets_[bucket.prev_bucket].last_elem->tail), *tail = bucket.last_elem->tail; for (Elem *e = head; e != tail; e = e->tail) if (e->key == key) return e; return NULL; // Not found. } } template inline typename HashList::Elem* HashList::New() { if (freed_head_) { Elem *ans = freed_head_; freed_head_ = freed_head_->tail; return ans; } else { Elem *tmp = new Elem[allocate_block_size_]; for (size_t i = 0; i+1 < allocate_block_size_; i++) tmp[i].tail = tmp+i+1; tmp[allocate_block_size_-1].tail = NULL; freed_head_ = tmp; allocated_.push_back(tmp); return this->New(); } } template HashList::~HashList() { // First test whether we had any memory leak within the // HashList, i.e. things for which the user did not call Delete(). size_t num_in_list = 0, num_allocated = 0; for (Elem *e = freed_head_; e != NULL; e = e->tail) num_in_list++; for (size_t i = 0; i < allocated_.size(); i++) { num_allocated += allocate_block_size_; delete[] allocated_[i]; } if (num_in_list != num_allocated) { KALDI_WARN << "Possible memory leak: " << num_in_list << " != " << num_allocated << ": you might have forgotten to call Delete on " << "some Elems"; } } template inline typename HashList::Elem* HashList::Insert(I key, T val) { size_t index = (static_cast(key) % hash_size_); HashBucket &bucket = buckets_[index]; // Check the element is existing or not. if (bucket.last_elem != NULL) { Elem *head = (bucket.prev_bucket == static_cast(-1) ? list_head_ : buckets_[bucket.prev_bucket].last_elem->tail), *tail = bucket.last_elem->tail; for (Elem *e = head; e != tail; e = e->tail) if (e->key == key) return e; } // This is a new element. Insert it. Elem *elem = New(); elem->key = key; elem->val = val; if (bucket.last_elem == NULL) { // Unoccupied bucket. Insert at // head of bucket list (which is tail of regular list, they go in // opposite directions). if (bucket_list_tail_ == static_cast(-1)) { // list was empty so this is the first elem. KALDI_ASSERT(list_head_ == NULL); list_head_ = elem; } else { // link in to the chain of Elems buckets_[bucket_list_tail_].last_elem->tail = elem; } elem->tail = NULL; bucket.last_elem = elem; bucket.prev_bucket = bucket_list_tail_; bucket_list_tail_ = index; } else { // Already-occupied bucket. Insert at tail of list of elements within // the bucket. elem->tail = bucket.last_elem->tail; bucket.last_elem->tail = elem; bucket.last_elem = elem; } return elem; } template void HashList::InsertMore(I key, T val) { size_t index = (static_cast(key) % hash_size_); HashBucket &bucket = buckets_[index]; Elem *elem = New(); elem->key = key; elem->val = val; KALDI_ASSERT(bucket.last_elem != NULL); // assume one element is already here if (bucket.last_elem->key == key) { // standard behavior: add as last element elem->tail = bucket.last_elem->tail; bucket.last_elem->tail = elem; bucket.last_elem = elem; return; } Elem *e = (bucket.prev_bucket == static_cast(-1) ? list_head_ : buckets_[bucket.prev_bucket].last_elem->tail); // find place to insert in linked list while (e != bucket.last_elem->tail && e->key != key) e = e->tail; KALDI_ASSERT(e->key == key); // not found? - should not happen elem->tail = e->tail; e->tail = elem; } } // end namespace kaldi #endif // KALDI_UTIL_HASH_LIST_INL_H_