You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

193 lines
6.3 KiB

  1. // util/hash-list-inl.h
  2. // Copyright 2009-2011 Microsoft Corporation
  3. // 2013 Johns Hopkins University (author: Daniel Povey)
  4. // See ../../COPYING for clarification regarding multiple authors
  5. //
  6. // Licensed under the Apache License, Version 2.0 (the "License");
  7. // you may not use this file except in compliance with the License.
  8. // You may obtain a copy of the License at
  9. //
  10. // http://www.apache.org/licenses/LICENSE-2.0
  11. //
  12. // THIS CODE IS PROVIDED *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
  13. // KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
  14. // WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
  15. // MERCHANTABLITY OR NON-INFRINGEMENT.
  16. // See the Apache 2 License for the specific language governing permissions and
  17. // limitations under the License.
  18. #ifndef KALDI_UTIL_HASH_LIST_INL_H_
  19. #define KALDI_UTIL_HASH_LIST_INL_H_
  20. // Do not include this file directly. It is included by fast-hash.h
  21. namespace kaldi {
  22. template <class I, class T>
  23. HashList<I, T>::HashList() {
  24. list_head_ = NULL;
  25. bucket_list_tail_ = static_cast<size_t>(-1); // invalid.
  26. hash_size_ = 0;
  27. freed_head_ = NULL;
  28. }
  29. template <class I, class T>
  30. void HashList<I, T>::SetSize(size_t size) {
  31. hash_size_ = size;
  32. KALDI_ASSERT(list_head_ == NULL &&
  33. bucket_list_tail_ ==
  34. static_cast<size_t>(-1)); // make sure empty.
  35. if (size > buckets_.size()) buckets_.resize(size, HashBucket(0, NULL));
  36. }
  37. template <class I, class T>
  38. typename HashList<I, T>::Elem* HashList<I, T>::Clear() {
  39. // Clears the hashtable and gives ownership of the currently contained list
  40. // to the user.
  41. for (size_t cur_bucket = bucket_list_tail_;
  42. cur_bucket != static_cast<size_t>(-1);
  43. cur_bucket = buckets_[cur_bucket].prev_bucket) {
  44. buckets_[cur_bucket].last_elem = NULL; // this is how we indicate "empty".
  45. }
  46. bucket_list_tail_ = static_cast<size_t>(-1);
  47. Elem* ans = list_head_;
  48. list_head_ = NULL;
  49. return ans;
  50. }
  51. template <class I, class T>
  52. const typename HashList<I, T>::Elem* HashList<I, T>::GetList() const {
  53. return list_head_;
  54. }
  55. template <class I, class T>
  56. inline void HashList<I, T>::Delete(Elem* e) {
  57. e->tail = freed_head_;
  58. freed_head_ = e;
  59. }
  60. template <class I, class T>
  61. inline typename HashList<I, T>::Elem* HashList<I, T>::Find(I key) {
  62. size_t index = (static_cast<size_t>(key) % hash_size_);
  63. HashBucket& bucket = buckets_[index];
  64. if (bucket.last_elem == NULL) {
  65. return NULL; // empty bucket.
  66. } else {
  67. Elem *head = (bucket.prev_bucket == static_cast<size_t>(-1)
  68. ? list_head_
  69. : buckets_[bucket.prev_bucket].last_elem->tail),
  70. *tail = bucket.last_elem->tail;
  71. for (Elem* e = head; e != tail; e = e->tail)
  72. if (e->key == key) return e;
  73. return NULL; // Not found.
  74. }
  75. }
  76. template <class I, class T>
  77. inline typename HashList<I, T>::Elem* HashList<I, T>::New() {
  78. if (freed_head_) {
  79. Elem* ans = freed_head_;
  80. freed_head_ = freed_head_->tail;
  81. return ans;
  82. } else {
  83. Elem* tmp = new Elem[allocate_block_size_];
  84. for (size_t i = 0; i + 1 < allocate_block_size_; i++)
  85. tmp[i].tail = tmp + i + 1;
  86. tmp[allocate_block_size_ - 1].tail = NULL;
  87. freed_head_ = tmp;
  88. allocated_.push_back(tmp);
  89. return this->New();
  90. }
  91. }
  92. template <class I, class T>
  93. HashList<I, T>::~HashList() {
  94. // First test whether we had any memory leak within the
  95. // HashList, i.e. things for which the user did not call Delete().
  96. size_t num_in_list = 0, num_allocated = 0;
  97. for (Elem* e = freed_head_; e != NULL; e = e->tail) num_in_list++;
  98. for (size_t i = 0; i < allocated_.size(); i++) {
  99. num_allocated += allocate_block_size_;
  100. delete[] allocated_[i];
  101. }
  102. if (num_in_list != num_allocated) {
  103. KALDI_WARN << "Possible memory leak: " << num_in_list
  104. << " != " << num_allocated
  105. << ": you might have forgotten to call Delete on "
  106. << "some Elems";
  107. }
  108. }
  109. template <class I, class T>
  110. inline typename HashList<I, T>::Elem* HashList<I, T>::Insert(I key, T val) {
  111. size_t index = (static_cast<size_t>(key) % hash_size_);
  112. HashBucket& bucket = buckets_[index];
  113. // Check the element is existing or not.
  114. if (bucket.last_elem != NULL) {
  115. Elem *head = (bucket.prev_bucket == static_cast<size_t>(-1)
  116. ? list_head_
  117. : buckets_[bucket.prev_bucket].last_elem->tail),
  118. *tail = bucket.last_elem->tail;
  119. for (Elem* e = head; e != tail; e = e->tail)
  120. if (e->key == key) return e;
  121. }
  122. // This is a new element. Insert it.
  123. Elem* elem = New();
  124. elem->key = key;
  125. elem->val = val;
  126. if (bucket.last_elem == NULL) { // Unoccupied bucket. Insert at
  127. // head of bucket list (which is tail of regular list, they go in
  128. // opposite directions).
  129. if (bucket_list_tail_ == static_cast<size_t>(-1)) {
  130. // list was empty so this is the first elem.
  131. KALDI_ASSERT(list_head_ == NULL);
  132. list_head_ = elem;
  133. } else {
  134. // link in to the chain of Elems
  135. buckets_[bucket_list_tail_].last_elem->tail = elem;
  136. }
  137. elem->tail = NULL;
  138. bucket.last_elem = elem;
  139. bucket.prev_bucket = bucket_list_tail_;
  140. bucket_list_tail_ = index;
  141. } else {
  142. // Already-occupied bucket. Insert at tail of list of elements within
  143. // the bucket.
  144. elem->tail = bucket.last_elem->tail;
  145. bucket.last_elem->tail = elem;
  146. bucket.last_elem = elem;
  147. }
  148. return elem;
  149. }
  150. template <class I, class T>
  151. void HashList<I, T>::InsertMore(I key, T val) {
  152. size_t index = (static_cast<size_t>(key) % hash_size_);
  153. HashBucket& bucket = buckets_[index];
  154. Elem* elem = New();
  155. elem->key = key;
  156. elem->val = val;
  157. KALDI_ASSERT(bucket.last_elem != NULL); // assume one element is already here
  158. if (bucket.last_elem->key == key) { // standard behavior: add as last element
  159. elem->tail = bucket.last_elem->tail;
  160. bucket.last_elem->tail = elem;
  161. bucket.last_elem = elem;
  162. return;
  163. }
  164. Elem* e = (bucket.prev_bucket == static_cast<size_t>(-1)
  165. ? list_head_
  166. : buckets_[bucket.prev_bucket].last_elem->tail);
  167. // find place to insert in linked list
  168. while (e != bucket.last_elem->tail && e->key != key) e = e->tail;
  169. KALDI_ASSERT(e->key == key); // not found? - should not happen
  170. elem->tail = e->tail;
  171. e->tail = elem;
  172. }
  173. } // end namespace kaldi
  174. #endif // KALDI_UTIL_HASH_LIST_INL_H_