1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #pragma once
6 
7 #include <zircon/assert.h>
8 #include <fbl/intrusive_container_utils.h>
9 #include <fbl/intrusive_pointer_traits.h>
10 #include <fbl/intrusive_single_list.h>
11 #include <fbl/macros.h>
12 
13 #include <utility>
14 
15 namespace fbl {
16 
17 // Fwd decl of sanity checker class used by tests.
18 namespace tests {
19 namespace intrusive_containers {
20 class HashTableChecker;
21 }  // namespace tests
22 }  // namespace intrusive_containers
23 
24 // DefaultHashTraits defines a default implementation of traits used to
25 // define the hash function for a hash table.
26 //
27 // At a minimum, a class or a struct which is to be used to define the
28 // hash traits of a hashtable must define...
29 //
30 // GetHash : A static method which take a constant reference to an instance of
31 //           the container's KeyType and returns an instance of the container's
32 //           HashType representing the hashed value of the key.  The value must
33 //           be on the range from [0, Container::kNumBuckets - 1]
34 //
35 // DefaultHashTraits generates a compliant implementation of hash traits taking
36 // its KeyType, ObjType, HashType and NumBuckets from template parameters.
37 // Users of DefaultHashTraits only need to implement a static method of ObjType
38 // named GetHash which takes a const reference to a KeyType and returns a
39 // HashType.  The default implementation will automatically mod by the number of
40 // buckets given in the template parameters.  If the user's hash function
41 // already automatically guarantees that the returned hash value will be in the
42 // proper range, he/she should implement their own hash traits to avoid the
43 // extra div/mod operation.
44 template <typename KeyType,
45           typename ObjType,
46           typename HashType,
47           HashType kNumBuckets>
48 struct DefaultHashTraits {
49     static_assert(is_unsigned_integer<HashType>::value, "HashTypes must be unsigned integers");
GetHashDefaultHashTraits50     static HashType GetHash(const KeyType& key) {
51         return static_cast<HashType>(ObjType::GetHash(key) % kNumBuckets);
52     }
53 };
54 
55 template <typename  _KeyType,
56           typename  _PtrType,
57           typename  _BucketType = SinglyLinkedList<_PtrType>,
58           typename  _HashType   = size_t,
59           _HashType _NumBuckets = 37,
60           typename  _KeyTraits  = DefaultKeyedObjectTraits<
61                                     _KeyType,
62                                     typename internal::ContainerPtrTraits<_PtrType>::ValueType>,
63           typename  _HashTraits = DefaultHashTraits<
64                                     _KeyType,
65                                     typename internal::ContainerPtrTraits<_PtrType>::ValueType,
66                                     _HashType,
67                                     _NumBuckets>>
68 class HashTable {
69 private:
70     // Private fwd decls of the iterator implementation.
71     template <typename IterTraits> class iterator_impl;
72     struct iterator_traits;
73     struct const_iterator_traits;
74 
75 public:
76     // Pointer types/traits
77     using PtrType      = _PtrType;
78     using PtrTraits    = internal::ContainerPtrTraits<PtrType>;
79     using ValueType    = typename PtrTraits::ValueType;
80 
81     // Key types/traits
82     using KeyType      = _KeyType;
83     using KeyTraits    = _KeyTraits;
84 
85     // Hash types/traits
86     using HashType     = _HashType;
87     using HashTraits   = _HashTraits;
88 
89     // Bucket types/traits
90     using BucketType   = _BucketType;
91     using NodeTraits   = typename BucketType::NodeTraits;
92 
93     // Declarations of the standard iterator types.
94     using iterator       = iterator_impl<iterator_traits>;
95     using const_iterator = iterator_impl<const_iterator_traits>;
96 
97     // An alias for the type of this specific HashTable<...> and its test sanity checker.
98     using ContainerType = HashTable<_KeyType, _PtrType, _BucketType, _HashType,
99                                     _NumBuckets, _KeyTraits, _HashTraits>;
100     using CheckerType   = ::fbl::tests::intrusive_containers::HashTableChecker;
101 
102     // The number of buckets should be a nice prime such as 37, 211, 389 unless
103     // The hash function is really good. Lots of cheap hash functions have
104     // hidden periods for which the mod with prime above 'mostly' fixes.
105     static constexpr HashType kNumBuckets = _NumBuckets;
106 
107     // Hash tables only support constant order erase if their underlying bucket
108     // type does.
109     static constexpr bool SupportsConstantOrderErase = BucketType::SupportsConstantOrderErase;
110     static constexpr bool SupportsConstantOrderSize = true;
111     static constexpr bool IsAssociative = true;
112     static constexpr bool IsSequenced = false;
113 
114     static_assert(kNumBuckets > 0, "Hash tables must have at least one bucket");
115     static_assert(is_unsigned_integer<HashType>::value, "HashTypes must be unsigned integers");
116 
HashTable()117     constexpr HashTable() {}
~HashTable()118     ~HashTable() { ZX_DEBUG_ASSERT(PtrTraits::IsManaged || is_empty()); }
119 
120     // Standard begin/end, cbegin/cend iterator accessors.
begin()121     iterator begin()              { return       iterator(this,       iterator::BEGIN); }
begin()122     const_iterator begin()  const { return const_iterator(this, const_iterator::BEGIN); }
cbegin()123     const_iterator cbegin() const { return const_iterator(this, const_iterator::BEGIN); }
124 
end()125     iterator end()              { return       iterator(this,       iterator::END); }
end()126     const_iterator end()  const { return const_iterator(this, const_iterator::END); }
cend()127     const_iterator cend() const { return const_iterator(this, const_iterator::END); }
128 
129     // make_iterator : construct an iterator out of a reference to an object.
make_iterator(ValueType & obj)130     iterator make_iterator(ValueType& obj) {
131         HashType ndx = GetHash(KeyTraits::GetKey(obj));
132         return iterator(this, ndx, buckets_[ndx].make_iterator(obj));
133     }
134 
insert(const PtrType & ptr)135     void insert(const PtrType& ptr) { insert(PtrType(ptr)); }
insert(PtrType && ptr)136     void insert(PtrType&& ptr) {
137         ZX_DEBUG_ASSERT(ptr != nullptr);
138         KeyType key = KeyTraits::GetKey(*ptr);
139         BucketType& bucket = GetBucket(key);
140 
141         // Duplicate keys are disallowed.  Debug assert if someone tries to to
142         // insert an element with a duplicate key.  If the user thought that
143         // there might be a duplicate key in the HashTable already, he/she
144         // should have used insert_or_find() instead.
145         ZX_DEBUG_ASSERT(FindInBucket(bucket, key).IsValid() == false);
146 
147         bucket.push_front(std::move(ptr));
148         ++count_;
149     }
150 
151     // insert_or_find
152     //
153     // Insert the element pointed to by ptr if it is not already in the
154     // HashTable, or find the element that the ptr collided with instead.
155     //
156     // 'iter' is an optional out parameter pointer to an iterator which
157     // will reference either the newly inserted item, or the item whose key
158     // collided with ptr.
159     //
160     // insert_or_find returns true if there was no collision and the item was
161     // successfully inserted, otherwise it returns false.
162     //
163     bool insert_or_find(const PtrType& ptr, iterator* iter = nullptr) {
164         return insert_or_find(PtrType(ptr), iter);
165     }
166 
167     bool insert_or_find(PtrType&& ptr, iterator* iter = nullptr) {
168         ZX_DEBUG_ASSERT(ptr != nullptr);
169         KeyType  key         = KeyTraits::GetKey(*ptr);
170         HashType ndx         = GetHash(key);
171         auto&    bucket      = buckets_[ndx];
172         auto     bucket_iter = FindInBucket(bucket, key);
173 
174         if (bucket_iter.IsValid()) {
175             if (iter) *iter = iterator(this, ndx, bucket_iter);
176             return false;
177         }
178 
179         bucket.push_front(std::move(ptr));
180         ++count_;
181         if (iter) *iter = iterator(this, ndx, bucket.begin());
182         return true;
183     }
184 
185     // insert_or_replace
186     //
187     // Find the element in the hashtable with the same key as *ptr and replace
188     // it with ptr, then return the pointer to the element which was replaced.
189     // If no element in the hashtable shares a key with *ptr, simply add ptr to
190     // the hashtable and return nullptr.
191     //
insert_or_replace(const PtrType & ptr)192     PtrType insert_or_replace(const PtrType& ptr) {
193         return insert_or_replace(PtrType(ptr));
194     }
195 
insert_or_replace(PtrType && ptr)196     PtrType insert_or_replace(PtrType&& ptr) {
197         ZX_DEBUG_ASSERT(ptr != nullptr);
198         KeyType  key    = KeyTraits::GetKey(*ptr);
199         HashType ndx    = GetHash(key);
200         auto&    bucket = buckets_[ndx];
201         auto     orig   = PtrTraits::GetRaw(ptr);
202 
203         PtrType replaced = bucket.replace_if(
204             [key](const ValueType& other) -> bool {
205                 return KeyTraits::EqualTo(key, KeyTraits::GetKey(other));
206             },
207             std::move(ptr));
208 
209         if (orig == PtrTraits::GetRaw(replaced)) {
210             bucket.push_front(std::move(replaced));
211             count_++;
212             return nullptr;
213         }
214 
215         return replaced;
216     }
217 
find(const KeyType & key)218     iterator find(const KeyType& key) {
219         HashType ndx         = GetHash(key);
220         auto&    bucket      = buckets_[ndx];
221         auto     bucket_iter = FindInBucket(bucket, key);
222 
223         return bucket_iter.IsValid() ? iterator(this, ndx, bucket_iter)
224                                      : iterator(this, iterator::END);
225     }
226 
find(const KeyType & key)227     const_iterator find(const KeyType& key) const {
228         HashType    ndx         = GetHash(key);
229         const auto& bucket      = buckets_[ndx];
230         auto        bucket_iter = FindInBucket(bucket, key);
231 
232         return bucket_iter.IsValid() ? const_iterator(this, ndx, bucket_iter)
233                                      : const_iterator(this, const_iterator::END);
234     }
235 
erase(const KeyType & key)236     PtrType erase(const KeyType& key) {
237         BucketType& bucket = GetBucket(key);
238 
239         PtrType ret = internal::KeyEraseUtils<BucketType, KeyTraits>::erase(bucket, key);
240         if (ret != nullptr)
241             --count_;
242 
243         return ret;
244     }
245 
erase(const iterator & iter)246     PtrType erase(const iterator& iter) {
247         if (!iter.IsValid())
248             return PtrType(nullptr);
249 
250         return direct_erase(buckets_[iter.bucket_ndx_], *iter);
251     }
252 
erase(ValueType & obj)253     PtrType erase(ValueType& obj) {
254         return direct_erase(GetBucket(obj), obj);
255     }
256 
257     // clear
258     //
259     // Clear out the all of the hashtable buckets.  For managed pointer types,
260     // this will release all references held by the hashtable to the objects
261     // which were in it.
clear()262     void clear() {
263         for (auto& e : buckets_)
264             e.clear();
265         count_ = 0;
266     }
267 
268     // clear_unsafe
269     //
270     // Perform a clear_unsafe on all buckets and reset the internal count to
271     // zero.  See comments in fbl/intrusive_single_list.h
272     // Think carefully before calling this!
clear_unsafe()273     void clear_unsafe() {
274         static_assert(PtrTraits::IsManaged == false,
275                      "clear_unsafe is not allowed for containers of managed pointers");
276 
277         for (auto& e : buckets_)
278             e.clear_unsafe();
279 
280         count_ = 0;
281     }
282 
size()283     size_t size()      const { return count_; }
is_empty()284     bool   is_empty()  const { return count_ == 0; }
285 
286     // erase_if
287     //
288     // Find the first member of the hash table which satisfies the predicate
289     // given by 'fn' and erase it from the list, returning a referenced pointer
290     // to the removed element.  Return nullptr if no member satisfies the
291     // predicate.
292     template <typename UnaryFn>
erase_if(UnaryFn fn)293     PtrType erase_if(UnaryFn fn) {
294         if (is_empty())
295             return PtrType(nullptr);
296 
297         for (HashType i = 0; i < kNumBuckets; ++i) {
298             auto& bucket = buckets_[i];
299             if (!bucket.is_empty()) {
300                 PtrType ret = bucket.erase_if(fn);
301                 if (ret != nullptr) {
302                     --count_;
303                     return ret;
304                 }
305             }
306         }
307 
308         return PtrType(nullptr);
309     }
310 
311     // find_if
312     //
313     // Find the first member of the hash table which satisfies the predicate
314     // given by 'fn' and return an iterator to it.  Return end() if no member
315     // satisfies the predicate.
316     template <typename UnaryFn>
find_if(UnaryFn fn)317     const_iterator find_if(UnaryFn fn) const {
318         for (auto iter = begin(); iter.IsValid(); ++iter)
319             if (fn(*iter))
320                 return iter;
321 
322         return end();
323     }
324 
325     template <typename UnaryFn>
find_if(UnaryFn fn)326     iterator find_if(UnaryFn fn) {
327         for (auto iter = begin(); iter.IsValid(); ++iter)
328             if (fn(*iter))
329                 return iter;
330 
331         return end();
332     }
333 
334 private:
335     // The traits of a non-const iterator
336     struct iterator_traits {
337         using RefType    = typename PtrTraits::RefType;
338         using RawPtrType = typename PtrTraits::RawPtrType;
339         using IterType   = typename BucketType::iterator;
340 
BucketBeginiterator_traits341         static IterType BucketBegin(BucketType& bucket) { return bucket.begin(); }
BucketEnditerator_traits342         static IterType BucketEnd  (BucketType& bucket) { return bucket.end(); }
343     };
344 
345     // The traits of a const iterator
346     struct const_iterator_traits {
347         using RefType    = typename PtrTraits::ConstRefType;
348         using RawPtrType = typename PtrTraits::ConstRawPtrType;
349         using IterType   = typename BucketType::const_iterator;
350 
BucketBeginconst_iterator_traits351         static IterType BucketBegin(const BucketType& bucket) { return bucket.cbegin(); }
BucketEndconst_iterator_traits352         static IterType BucketEnd  (const BucketType& bucket) { return bucket.cend(); }
353     };
354 
355     // The shared implementation of the iterator
356     template <class IterTraits>
357     class iterator_impl {
358     public:
iterator_impl()359         iterator_impl() { }
iterator_impl(const iterator_impl & other)360         iterator_impl(const iterator_impl& other) {
361             hash_table_ = other.hash_table_;
362             bucket_ndx_ = other.bucket_ndx_;
363             iter_       = other.iter_;
364         }
365 
366         iterator_impl& operator=(const iterator_impl& other) {
367             hash_table_ = other.hash_table_;
368             bucket_ndx_ = other.bucket_ndx_;
369             iter_       = other.iter_;
370             return *this;
371         }
372 
IsValid()373         bool IsValid() const { return iter_.IsValid(); }
374         bool operator==(const iterator_impl& other) const { return iter_ == other.iter_; }
375         bool operator!=(const iterator_impl& other) const { return iter_ != other.iter_; }
376 
377         // Prefix
378         iterator_impl& operator++() {
379             if (!IsValid()) return *this;
380             ZX_DEBUG_ASSERT(hash_table_);
381 
382             // Bump the bucket iterator and go looking for a new bucket if the
383             // iterator has become invalid.
384             ++iter_;
385             advance_if_invalid_iter();
386 
387             return *this;
388         }
389 
390         iterator_impl& operator--() {
391             // If we have never been bound to a HashTable instance, the we had
392             // better be invalid.
393             if (!hash_table_) {
394                 ZX_DEBUG_ASSERT(!IsValid());
395                 return *this;
396             }
397 
398             // Back up the bucket iterator.  If it is still valid, then we are done.
399             --iter_;
400             if (iter_.IsValid())
401                 return *this;
402 
403             // If the iterator is invalid after backing up, check previous
404             // buckets to see if they contain any nodes.
405             while (bucket_ndx_) {
406                 --bucket_ndx_;
407                 auto& bucket = GetBucket(bucket_ndx_);
408                 if (!bucket.is_empty()) {
409                     iter_ = --IterTraits::BucketEnd(bucket);
410                     ZX_DEBUG_ASSERT(iter_.IsValid());
411                     return *this;
412                 }
413             }
414 
415             // Looks like we have backed up past the beginning.  Update the
416             // bookkeeping to point at the end of the last bucket.
417             bucket_ndx_ = kNumBuckets - 1;
418             iter_ = IterTraits::BucketEnd(GetBucket(bucket_ndx_));
419 
420             return *this;
421         }
422 
423         // Postfix
424         iterator_impl operator++(int) {
425             iterator_impl ret(*this);
426             ++(*this);
427             return ret;
428         }
429 
430         iterator_impl operator--(int) {
431             iterator_impl ret(*this);
432             --(*this);
433             return ret;
434         }
435 
CopyPointer()436         typename PtrTraits::PtrType CopyPointer()    const { return iter_.CopyPointer(); }
437         typename IterTraits::RefType operator*()     const { return iter_.operator*(); }
438         typename IterTraits::RawPtrType operator->() const { return iter_.operator->(); }
439 
440     private:
441         friend ContainerType;
442         using IterType = typename IterTraits::IterType;
443 
444         enum BeginTag { BEGIN };
445         enum EndTag { END };
446 
iterator_impl(const ContainerType * hash_table,BeginTag)447         iterator_impl(const ContainerType* hash_table, BeginTag)
448             : hash_table_(hash_table),
449               bucket_ndx_(0),
450               iter_(IterTraits::BucketBegin(GetBucket(0))) {
451             advance_if_invalid_iter();
452         }
453 
iterator_impl(const ContainerType * hash_table,EndTag)454         iterator_impl(const ContainerType* hash_table, EndTag)
455             : hash_table_(hash_table),
456               bucket_ndx_(kNumBuckets - 1),
457               iter_(IterTraits::BucketEnd(GetBucket(kNumBuckets - 1))) { }
458 
iterator_impl(const ContainerType * hash_table,HashType bucket_ndx,const IterType & iter)459         iterator_impl(const ContainerType* hash_table, HashType bucket_ndx, const IterType& iter)
460             : hash_table_(hash_table),
461               bucket_ndx_(bucket_ndx),
462               iter_(iter) { }
463 
GetBucket(HashType ndx)464         BucketType& GetBucket(HashType ndx) {
465             return const_cast<ContainerType*>(hash_table_)->buckets_[ndx];
466         }
467 
advance_if_invalid_iter()468         void advance_if_invalid_iter() {
469             // If the iterator has run off the end of it's current bucket, then
470             // check to see if there are nodes in any of the remaining buckets.
471             if (!iter_.IsValid()) {
472                 while (bucket_ndx_ < (kNumBuckets - 1)) {
473                     ++bucket_ndx_;
474                     auto& bucket = GetBucket(bucket_ndx_);
475 
476                     if (!bucket.is_empty()) {
477                         iter_ = IterTraits::BucketBegin(bucket);
478                         ZX_DEBUG_ASSERT(iter_.IsValid());
479                         break;
480                     } else if (bucket_ndx_ == (kNumBuckets - 1)) {
481                         iter_ = IterTraits::BucketEnd(bucket);
482                     }
483                 }
484             }
485         }
486 
487         const ContainerType* hash_table_ = nullptr;
488         HashType bucket_ndx_ = 0;
489         IterType iter_;
490     };
491 
direct_erase(BucketType & bucket,ValueType & obj)492     PtrType direct_erase(BucketType& bucket, ValueType& obj) {
493         PtrType ret = internal::DirectEraseUtils<BucketType>::erase(bucket, obj);
494 
495         if (ret != nullptr)
496             --count_;
497 
498         return ret;
499     }
500 
FindInBucket(BucketType & bucket,const KeyType & key)501     static typename BucketType::iterator FindInBucket(BucketType& bucket,
502                                                       const KeyType& key) {
503         return bucket.find_if(
504             [key](const ValueType& other) -> bool {
505                 return KeyTraits::EqualTo(key, KeyTraits::GetKey(other));
506             });
507     }
508 
FindInBucket(const BucketType & bucket,const KeyType & key)509     static typename BucketType::const_iterator FindInBucket(const BucketType& bucket,
510                                                             const KeyType& key) {
511         return bucket.find_if(
512             [key](const ValueType& other) -> bool {
513                 return KeyTraits::EqualTo(key, KeyTraits::GetKey(other));
514             });
515     }
516 
517     // The test framework's 'checker' class is our friend.
518     friend CheckerType;
519 
520     // Iterators need to access our bucket array in order to iterate.
521     friend iterator;
522     friend const_iterator;
523 
524     // Hash tables may not currently be copied, assigned or moved.
525     DISALLOW_COPY_ASSIGN_AND_MOVE(HashTable);
526 
GetBucket(const KeyType & key)527     BucketType& GetBucket(const KeyType& key) { return buckets_[GetHash(key)]; }
GetBucket(const ValueType & obj)528     BucketType& GetBucket(const ValueType& obj) { return GetBucket(KeyTraits::GetKey(obj)); }
529 
GetHash(const KeyType & obj)530     static HashType GetHash(const KeyType& obj) {
531         HashType ret = HashTraits::GetHash(obj);
532         ZX_DEBUG_ASSERT((ret >= 0) && (ret < kNumBuckets));
533         return ret;
534     }
535 
536     size_t count_ = 0UL;
537     BucketType buckets_[kNumBuckets];
538 };
539 
540 // Explicit declaration of constexpr storage.
541 #define HASH_TABLE_PROP(_type, _name) \
542 template <typename KeyType, typename PtrType, typename BucketType, typename HashType, \
543           HashType NumBuckets, typename KeyTraits, typename HashTraits> \
544 constexpr _type HashTable<KeyType, PtrType, BucketType, HashType, \
545                           NumBuckets, KeyTraits, HashTraits>::_name
546 
547 HASH_TABLE_PROP(HashType, kNumBuckets);
548 HASH_TABLE_PROP(bool, SupportsConstantOrderErase);
549 HASH_TABLE_PROP(bool, SupportsConstantOrderSize);
550 HASH_TABLE_PROP(bool, IsAssociative);
551 HASH_TABLE_PROP(bool, IsSequenced);
552 
553 #undef HASH_TABLE_PROP
554 
555 }  // namespace fbl
556