diff options
author | Alexander Fokin <apfokin@gmail.com> | 2022-02-10 16:45:38 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:45:38 +0300 |
commit | bf9e69a933f89af083d895185f01ed65e4d90766 (patch) | |
tree | b2cc84ee7850122e7ccf51d0ea21e4fa7e7a5685 /util/generic/hash.h | |
parent | 863a59a65247c24db7cb06789bc5cf79d04da32f (diff) | |
download | ydb-bf9e69a933f89af083d895185f01ed65e4d90766.tar.gz |
Restoring authorship annotation for Alexander Fokin <apfokin@gmail.com>. Commit 2 of 2.
Diffstat (limited to 'util/generic/hash.h')
-rw-r--r-- | util/generic/hash.h | 392 |
1 files changed, 196 insertions, 196 deletions
diff --git a/util/generic/hash.h b/util/generic/hash.h index 78572070a9..e46db21fa9 100644 --- a/util/generic/hash.h +++ b/util/generic/hash.h @@ -1,6 +1,6 @@ #pragma once -#include "fwd.h" +#include "fwd.h" #include "mapfindptr.h" #include <util/memory/alloc.h> @@ -19,8 +19,8 @@ #include <cstdlib> -#include "hash_primes.h" - +#include "hash_primes.h" + struct TSelect1st { template <class TPair> inline const typename TPair::first_type& operator()(const TPair& x) const { @@ -37,10 +37,10 @@ struct __yhashtable_node { * This trick makes it possible to use only one node pointer in a hash table * iterator. */ __yhashtable_node* next; - + /** Value stored in a node. */ Value val; - + __yhashtable_node& operator=(const __yhashtable_node&) = delete; }; @@ -148,19 +148,19 @@ struct __yhashtable_const_iterator { } }; -/** - * This class saves some space in allocator-based containers for the most common - * use case of empty allocators. This is achieved thanks to the application of - * empty base class optimization (aka EBCO). - */ +/** + * This class saves some space in allocator-based containers for the most common + * use case of empty allocators. This is achieved thanks to the application of + * empty base class optimization (aka EBCO). + */ template <class Alloc> -class _allocator_base: private Alloc { -public: +class _allocator_base: private Alloc { +public: _allocator_base(const Alloc& other) : Alloc(other) { } - + Alloc& _get_alloc() { return static_cast<Alloc&>(*this); } @@ -170,45 +170,45 @@ public: void _set_alloc(const Alloc& allocator) { _get_alloc() = allocator; } - + void swap(_allocator_base& other) { DoSwap(_get_alloc(), other._get_alloc()); } -}; - -/** +}; + +/** * Wrapper for an array of THashTable buckets. - * - * Is better than vector for this particular use case. Main differences: - * - Occupies one less word on stack. + * + * Is better than vector for this particular use case. Main differences: + * - Occupies one less word on stack. * - Doesn't even try to initialize its elements. It is THashTable's responsibility. * - Presents a better interface in relation to THashTable's marker element trick. - * - * Internally this class is just a pointer-size pair, and the data on the heap - * has the following structure: - * - * +----------+----------------------+----------+-------------------------+ - * | raw_size | elements ... | marker | unused space [optional] | - * +----------+----------------------+----------+-------------------------+ - * ^ ^ - * | | - * Data points here end() points here - * - * `raw_size` stores the size of the allocated memory block. It is used to - * support resizing without reallocation. - * + * + * Internally this class is just a pointer-size pair, and the data on the heap + * has the following structure: + * + * +----------+----------------------+----------+-------------------------+ + * | raw_size | elements ... | marker | unused space [optional] | + * +----------+----------------------+----------+-------------------------+ + * ^ ^ + * | | + * Data points here end() points here + * + * `raw_size` stores the size of the allocated memory block. It is used to + * support resizing without reallocation. + * * `marker` is a special marker element that is set by the THashTable that is - * then used in iterator implementation to know when the end is reached. - * - * Unused space at the end of the memory block may not be present. - */ + * then used in iterator implementation to know when the end is reached. + * + * Unused space at the end of the memory block may not be present. + */ template <class T, class Alloc> class _yhashtable_buckets: private _allocator_base<Alloc> { using base_type = _allocator_base<Alloc>; - + static_assert(sizeof(T) == sizeof(size_t), "T is expected to be the same size as size_t."); - -public: + +public: using allocator_type = Alloc; using value_type = T; using pointer = T*; @@ -220,55 +220,55 @@ public: using size_type = size_t; using difference_type = ptrdiff_t; using TBucketDivisor = ::NPrivate::THashDivisor; - + _yhashtable_buckets(const Alloc& other) : base_type(other) , Data(nullptr) , Size() { } - + ~_yhashtable_buckets() { Y_ASSERT(!Data); } - + void initialize_dynamic(TBucketDivisor size) { Y_ASSERT(!Data); - + Data = this->_get_alloc().allocate(size() + 2) + 1; Size = size; - + *reinterpret_cast<size_type*>(Data - 1) = size() + 2; } - + void deinitialize_dynamic() { Y_ASSERT(Data); - + this->_get_alloc().deallocate(Data - 1, *reinterpret_cast<size_type*>(Data - 1)); Data = pointer(); Size = TBucketDivisor(); } - + void initialize_static(pointer data, TBucketDivisor size) { Y_ASSERT(!Data && data && size() >= 1); - + Data = data; Size = size; } - + void deinitialize_static() { Y_ASSERT(Data); - + Data = pointer(); Size = TBucketDivisor(); } - + void resize_noallocate(TBucketDivisor size) { Y_ASSERT(size() <= capacity()); - + Size = size; } - + iterator begin() { return Data; } @@ -281,14 +281,14 @@ public: const_iterator end() const { return Data + Size(); } - + pointer data() { return Data; } const_pointer data() const { return Data; } - + size_type size() const { return Size(); } @@ -301,57 +301,57 @@ public: int BucketDivisorHint() const { return +Size.Hint; } - + allocator_type get_allocator() const { return this->_get_alloc(); } - + const_reference operator[](size_type index) const { Y_ASSERT(index <= Size()); - + return *(Data + index); } - + reference operator[](size_type index) { Y_ASSERT(index <= Size()); - + return *(Data + index); } - + void swap(_yhashtable_buckets& other) { base_type::swap(other); DoSwap(Data, other.Data); DoSwap(Size, other.Size); } - -private: + +private: /** Pointer to the first element of the buckets array. */ pointer Data; - + /** Size of the buckets array. Doesn't take the marker element at the end into account. */ TBucketDivisor Size; -}; - -/** +}; + +/** * This class saves one word in THashTable for the most common use case of empty - * functors. The exact implementation picks a specialization with storage allocated - * for the functors if those are non-empty, and another specialization that creates - * functors on the fly if they are empty. It is expected that empty functors have - * trivial constructors. - * - * Note that this is basically the only way to do it portably. Another option is - * multiple inheritance from empty functors, but MSVC's empty base class - * optimization chokes up on multiple empty bases, and we're already using - * EBCO in _allocator_base. - * - * Note that there are no specializations for the case when only one or two - * of the functors are empty as this is a case that's just way too rare. - */ + * functors. The exact implementation picks a specialization with storage allocated + * for the functors if those are non-empty, and another specialization that creates + * functors on the fly if they are empty. It is expected that empty functors have + * trivial constructors. + * + * Note that this is basically the only way to do it portably. Another option is + * multiple inheritance from empty functors, but MSVC's empty base class + * optimization chokes up on multiple empty bases, and we're already using + * EBCO in _allocator_base. + * + * Note that there are no specializations for the case when only one or two + * of the functors are empty as this is a case that's just way too rare. + */ template <class HashFcn, class ExtractKey, class EqualKey, class Alloc, bool IsEmpty = std::is_empty<HashFcn>::value&& std::is_empty<ExtractKey>::value&& std::is_empty<EqualKey>::value> class _yhashtable_base: public _allocator_base<Alloc> { using base_type = _allocator_base<Alloc>; -public: +public: _yhashtable_base(const HashFcn& hash, const ExtractKey& extract, const EqualKey& equals, const Alloc& alloc) : base_type(alloc) , hash_(hash) @@ -359,7 +359,7 @@ public: , equals_(equals) { } - + const EqualKey& _get_key_eq() const { return equals_; } @@ -369,7 +369,7 @@ public: void _set_key_eq(const EqualKey& equals) { this->equals_ = equals; } - + const ExtractKey& _get_key_extract() const { return extract_; } @@ -379,7 +379,7 @@ public: void _set_key_extract(const ExtractKey& extract) { this->extract_ = extract; } - + const HashFcn& _get_hash_fun() const { return hash_; } @@ -389,63 +389,63 @@ public: void _set_hash_fun(const HashFcn& hash) { this->hash_ = hash; } - + void swap(_yhashtable_base& other) { base_type::swap(other); DoSwap(equals_, other.equals_); DoSwap(extract_, other.extract_); DoSwap(hash_, other.hash_); } - -private: + +private: HashFcn hash_; ExtractKey extract_; EqualKey equals_; -}; - +}; + template <class HashFcn, class ExtractKey, class EqualKey, class Alloc> class _yhashtable_base<HashFcn, ExtractKey, EqualKey, Alloc, true>: public _allocator_base<Alloc> { using base_type = _allocator_base<Alloc>; - -public: + +public: _yhashtable_base(const HashFcn&, const ExtractKey&, const EqualKey&, const Alloc& alloc) : base_type(alloc) { } - + EqualKey _get_key_eq() const { return EqualKey(); } void _set_key_eq(const EqualKey&) { } - + ExtractKey _get_key_extract() const { return ExtractKey(); } void _set_key_extract(const ExtractKey&) { } - + HashFcn _get_hash_fun() const { return HashFcn(); } void _set_hash_fun(const HashFcn&) { } - + void swap(_yhashtable_base& other) { base_type::swap(other); } -}; - -template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc> +}; + +template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc> struct _yhashtable_traits { using node = __yhashtable_node<Value>; - + using node_allocator_type = TReboundAllocator<Alloc, node>; using nodep_allocator_type = TReboundAllocator<Alloc, node*>; - + using base_type = _yhashtable_base<HashFcn, ExtractKey, EqualKey, node_allocator_type>; -}; - +}; + extern const void* const _yhashtable_empty_data[]; template <class Value, class Key, class HashFcn, class ExtractKey, class EqualKey, class Alloc> @@ -456,7 +456,7 @@ class THashTable: private _yhashtable_traits<Value, Key, HashFcn, ExtractKey, Eq using nodep_allocator_type = typename traits_type::nodep_allocator_type; using buckets_type = _yhashtable_buckets<node*, nodep_allocator_type>; using TBucketDivisor = ::NPrivate::THashDivisor; - + public: using key_type = Key; using value_type = Value; @@ -497,16 +497,16 @@ private: auto get_key(const ValueL& value) const -> decltype(ExtractKey()(value)) { return this->_get_key_extract()(value); } - + node* get_node() { - node* result = this->_get_alloc().allocate(1); + node* result = this->_get_alloc().allocate(1); Y_ASSERT((reinterpret_cast<uintptr_t>(result) & 1) == 0); /* We're using the last bit of the node pointer. */ - return result; + return result; } void put_node(node* p) { this->_get_alloc().deallocate(p, 1); } - + buckets_type buckets; size_type num_elements; @@ -526,7 +526,7 @@ public: { initialize_buckets(buckets, 0); } - + THashTable(size_type n, const HashFcn& hf, const EqualKey& eql, const ExtractKey& ext) : base_type(hf, ext, eql, node_allocator_type()) , buckets(nodep_allocator_type()) @@ -550,7 +550,7 @@ public: , num_elements(0) { initialize_buckets(buckets, n); - } + } THashTable(const THashTable& ht) : base_type(ht._get_hash_fun(), ht._get_key_extract(), ht._get_key_eq(), ht._get_alloc()) @@ -559,12 +559,12 @@ public: { if (ht.empty()) { initialize_buckets(buckets, 0); - } else { + } else { initialize_buckets_dynamic(buckets, ht.buckets.ExtSize()); copy_from_dynamic(ht); - } + } } - + THashTable(THashTable&& ht) noexcept : base_type(ht._get_hash_fun(), ht._get_key_extract(), ht._get_key_eq(), ht._get_alloc()) , buckets(ht.buckets.get_allocator()) @@ -580,16 +580,16 @@ public: this->_set_hash_fun(ht._get_hash_fun()); this->_set_key_eq(ht._get_key_eq()); this->_set_key_extract(ht._get_key_extract()); - /* We don't copy allocator for a reason. */ - - if (ht.empty()) { - /* Some of the old code in Arcadia works around the behavior in - * clear() by invoking operator= with empty hash as an argument. - * It's expected that this will deallocate the buckets array, so - * this is what we have to do here. */ - deinitialize_buckets(buckets); - initialize_buckets(buckets, 0); - } else { + /* We don't copy allocator for a reason. */ + + if (ht.empty()) { + /* Some of the old code in Arcadia works around the behavior in + * clear() by invoking operator= with empty hash as an argument. + * It's expected that this will deallocate the buckets array, so + * this is what we have to do here. */ + deinitialize_buckets(buckets); + initialize_buckets(buckets, 0); + } else { if (buckets.capacity() > ht.buckets.size()) { buckets.resize_noallocate(ht.buckets.ExtSize()); } else { @@ -604,8 +604,8 @@ public: } THashTable& operator=(THashTable&& ht) noexcept { - basic_clear(); - swap(ht); + basic_clear(); + swap(ht); return *this; } @@ -668,13 +668,13 @@ public: } template <class OtherValue> - std::pair<iterator, bool> insert_unique(const OtherValue& obj) { + std::pair<iterator, bool> insert_unique(const OtherValue& obj) { reserve(num_elements + 1); return insert_unique_noresize(obj); } template <class OtherValue> - iterator insert_equal(const OtherValue& obj) { + iterator insert_equal(const OtherValue& obj) { reserve(num_elements + 1); return emplace_equal_noresize(obj); } @@ -686,7 +686,7 @@ public: } template <class OtherValue> - iterator insert_direct(const OtherValue& obj, insert_ctx ins) { + iterator insert_direct(const OtherValue& obj, insert_ctx ins) { return emplace_direct(ins, obj); } @@ -712,9 +712,9 @@ public: template <typename... Args> std::pair<iterator, bool> emplace_unique_noresize(Args&&... args); - template <class OtherValue> - std::pair<iterator, bool> insert_unique_noresize(const OtherValue& obj); - + template <class OtherValue> + std::pair<iterator, bool> insert_unique_noresize(const OtherValue& obj); + template <typename... Args> iterator emplace_equal_noresize(Args&&... args); @@ -759,10 +759,10 @@ public: } template <class OtherValue> - reference find_or_insert(const OtherValue& v); + reference find_or_insert(const OtherValue& v); template <class OtherKey> - iterator find(const OtherKey& key) { + iterator find(const OtherKey& key) { size_type n = bkt_num_key(key); node* first; for (first = buckets[n]; @@ -774,7 +774,7 @@ public: } template <class OtherKey> - const_iterator find(const OtherKey& key) const { + const_iterator find(const OtherKey& key) const { size_type n = bkt_num_key(key); const node* first; for (first = buckets[n]; @@ -786,10 +786,10 @@ public: } template <class OtherKey> - iterator find_i(const OtherKey& key, insert_ctx& ins); + iterator find_i(const OtherKey& key, insert_ctx& ins); template <class OtherKey> - size_type count(const OtherKey& key) const { + size_type count(const OtherKey& key) const { const size_type n = bkt_num_key(key); size_type result = 0; @@ -801,17 +801,17 @@ public: } template <class OtherKey> - std::pair<iterator, iterator> equal_range(const OtherKey& key); + std::pair<iterator, iterator> equal_range(const OtherKey& key); template <class OtherKey> - std::pair<const_iterator, const_iterator> equal_range(const OtherKey& key) const; - + std::pair<const_iterator, const_iterator> equal_range(const OtherKey& key) const; + template <class OtherKey> - size_type erase(const OtherKey& key); - + size_type erase(const OtherKey& key); + template <class OtherKey> - size_type erase_one(const OtherKey& key); - + size_type erase_one(const OtherKey& key); + // void (instead of iterator) is intended, see http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2023.pdf void erase(const iterator& it); void erase(iterator first, iterator last); @@ -822,21 +822,21 @@ public: bool reserve(size_type num_elements_hint); void basic_clear(); - /** - * Clears the hashtable without deallocating the nodes. - * - * This might come in handy with non-standard allocators, e.g. a pool - * allocator with a pool that is then cleared manually, thus releasing all - * the nodes at once. - */ - void release_nodes() { - if (empty()) - return; /* Need this check because empty buckets may reside in read-only memory. */ - - clear_buckets(buckets); - num_elements = 0; - } - + /** + * Clears the hashtable without deallocating the nodes. + * + * This might come in handy with non-standard allocators, e.g. a pool + * allocator with a pool that is then cleared manually, thus releasing all + * the nodes at once. + */ + void release_nodes() { + if (empty()) + return; /* Need this check because empty buckets may reside in read-only memory. */ + + clear_buckets(buckets); + num_elements = 0; + } + // implemented in save_stl.h template <class KeySaver> int save_for_st(IOutputStream* stream, KeySaver& ks, sthash<int, int, THash<int>, TEqualTo<int>, typename KeySaver::TSizeType>* stHash = nullptr) const; @@ -852,7 +852,7 @@ public: } } } - + /** * Clears the hashtable and tries to reasonably downsize it. Note that * downsizing is mainly for the following use case: @@ -884,36 +884,36 @@ private: } else { TBucketDivisor size = HashBucketCountExt(sizeHint); Y_ASSERT(size() >= 7); - + initialize_buckets_dynamic(buckets, size); } - } + } static void initialize_buckets_dynamic(buckets_type& buckets, TBucketDivisor size) { buckets.initialize_dynamic(size); memset(buckets.data(), 0, size() * sizeof(*buckets.data())); buckets[size()] = (node*)1; } - + static void deinitialize_buckets(buckets_type& buckets) { if (buckets.size() == 1) { buckets.deinitialize_static(); } else { buckets.deinitialize_dynamic(); } - } - + } + static void clear_buckets(buckets_type& buckets) { memset(buckets.data(), 0, buckets.size() * sizeof(*buckets.data())); } - + template <class OtherKey> - size_type bkt_num_key(const OtherKey& key) const { + size_type bkt_num_key(const OtherKey& key) const { return bkt_num_key(key, buckets.ExtSize()); } template <class OtherValue> - size_type bkt_num(const OtherValue& obj) const { + size_type bkt_num(const OtherValue& obj) const { return bkt_num_key(get_key(obj)); } @@ -1019,7 +1019,7 @@ std::pair<typename THashTable<V, K, HF, Ex, Eq, A>::iterator, bool> THashTable<V } template <class V, class K, class HF, class Ex, class Eq, class A> -template <class OtherValue> +template <class OtherValue> std::pair<typename THashTable<V, K, HF, Ex, Eq, A>::iterator, bool> THashTable<V, K, HF, Ex, Eq, A>::insert_unique_noresize(const OtherValue& obj) { const size_type n = bkt_num(obj); node* first = buckets[n]; @@ -1063,7 +1063,7 @@ __yhashtable_iterator<V> THashTable<V, K, HF, Ex, Eq, A>::emplace_equal_noresize } template <class V, class K, class HF, class Ex, class Eq, class A> -template <class OtherValue> +template <class OtherValue> typename THashTable<V, K, HF, Ex, Eq, A>::reference THashTable<V, K, HF, Ex, Eq, A>::find_or_insert(const OtherValue& v) { reserve(num_elements + 1); @@ -1083,7 +1083,7 @@ typename THashTable<V, K, HF, Ex, Eq, A>::reference THashTable<V, K, HF, Ex, Eq, } template <class V, class K, class HF, class Ex, class Eq, class A> -template <class OtherKey> +template <class OtherKey> __yhashtable_iterator<V> THashTable<V, K, HF, Ex, Eq, A>::find_i(const OtherKey& key, insert_ctx& ins) { size_type n = bkt_num_key(key); ins = &buckets[n]; @@ -1097,7 +1097,7 @@ __yhashtable_iterator<V> THashTable<V, K, HF, Ex, Eq, A>::find_i(const OtherKey& } template <class V, class K, class HF, class Ex, class Eq, class A> -template <class OtherKey> +template <class OtherKey> std::pair<__yhashtable_iterator<V>, __yhashtable_iterator<V>> THashTable<V, K, HF, Ex, Eq, A>::equal_range(const OtherKey& key) { using pii = std::pair<iterator, iterator>; const size_type n = bkt_num_key(key); @@ -1120,7 +1120,7 @@ std::pair<__yhashtable_iterator<V>, __yhashtable_iterator<V>> THashTable<V, K, H } template <class V, class K, class HF, class Ex, class Eq, class A> -template <class OtherKey> +template <class OtherKey> std::pair<__yhashtable_const_iterator<V>, __yhashtable_const_iterator<V>> THashTable<V, K, HF, Ex, Eq, A>::equal_range(const OtherKey& key) const { using pii = std::pair<const_iterator, const_iterator>; const size_type n = bkt_num_key(key); @@ -1144,7 +1144,7 @@ std::pair<__yhashtable_const_iterator<V>, __yhashtable_const_iterator<V>> THashT } template <class V, class K, class HF, class Ex, class Eq, class A> -template <class OtherKey> +template <class OtherKey> typename THashTable<V, K, HF, Ex, Eq, A>::size_type THashTable<V, K, HF, Ex, Eq, A>::erase(const OtherKey& key) { const size_type n = bkt_num_key(key); node* first = buckets[n]; @@ -1176,7 +1176,7 @@ typename THashTable<V, K, HF, Ex, Eq, A>::size_type THashTable<V, K, HF, Ex, Eq, } template <class V, class K, class HF, class Ex, class Eq, class A> -template <class OtherKey> +template <class OtherKey> typename THashTable<V, K, HF, Ex, Eq, A>::size_type THashTable<V, K, HF, Ex, Eq, A>::erase_one(const OtherKey& key) { const size_type n = bkt_num_key(key); node* first = buckets[n]; @@ -1287,7 +1287,7 @@ bool THashTable<V, K, HF, Ex, Eq, A>::reserve(size_type num_elements_hint) { first = buckets[bucket]; } } - + buckets.swap(tmp); deinitialize_buckets(tmp); @@ -1365,7 +1365,7 @@ void THashTable<V, K, HF, Ex, Eq, A>::basic_clear() { template <class V, class K, class HF, class Ex, class Eq, class A> void THashTable<V, K, HF, Ex, Eq, A>::copy_from_dynamic(const THashTable& ht) { Y_ASSERT(buckets.size() == ht.buckets.size() && !ht.empty()); - + #ifdef __STL_USE_EXCEPTIONS try { #endif /* __STL_USE_EXCEPTIONS */ @@ -1391,9 +1391,9 @@ void THashTable<V, K, HF, Ex, Eq, A>::copy_from_dynamic(const THashTable& ht) { } namespace NPrivate { - template <class Key> + template <class Key> inline TString MapKeyToString(const Key&) { - return TypeName<Key>(); + return TypeName<Key>(); } TString MapKeyToString(TStringBuf key); @@ -1649,7 +1649,7 @@ public: template <class TheKey> const T& at(const TheKey& key) const { - using namespace ::NPrivate; + using namespace ::NPrivate; const_iterator it = find(key); if (Y_UNLIKELY(it == end())) { @@ -1661,7 +1661,7 @@ public: template <class TheKey> T& at(const TheKey& key) { - using namespace ::NPrivate; + using namespace ::NPrivate; iterator it = find(key); if (Y_UNLIKELY(it == end())) { @@ -1706,14 +1706,14 @@ public: void basic_clear() { rep.basic_clear(); } - void release_nodes() { - rep.release_nodes(); - } + void release_nodes() { + rep.release_nodes(); + } // if (stHash != NULL) bucket_count() must be equal to stHash->bucket_count() template <class KeySaver> int save_for_st(IOutputStream* stream, KeySaver& ks, sthash<int, int, THash<int>, TEqualTo<int>, typename KeySaver::TSizeType>* stHash = nullptr) const { - return rep.template save_for_st<KeySaver>(stream, ks, stHash); + return rep.template save_for_st<KeySaver>(stream, ks, stHash); } public: @@ -1748,12 +1748,12 @@ inline bool operator==(const THashMap<Key, T, HashFcn, EqualKey, Alloc>& hm1, co return true; } -template <class Key, class T, class HashFcn, class EqualKey, class Alloc> +template <class Key, class T, class HashFcn, class EqualKey, class Alloc> inline bool operator!=(const THashMap<Key, T, HashFcn, EqualKey, Alloc>& hm1, const THashMap<Key, T, HashFcn, EqualKey, Alloc>& hm2) { - return !(hm1 == hm2); -} - -template <class Key, class T, class HashFcn, class EqualKey, class Alloc> + return !(hm1 == hm2); +} + +template <class Key, class T, class HashFcn, class EqualKey, class Alloc> class THashMultiMap { private: using ht = THashTable<std::pair<const Key, T>, Key, HashFcn, TSelect1st, EqualKey, Alloc>; @@ -1972,9 +1972,9 @@ public: void basic_clear() { rep.basic_clear(); } - void release_nodes() { - rep.release_nodes(); - } + void release_nodes() { + rep.release_nodes(); + } // if (stHash != NULL) bucket_count() must be equal to stHash->bucket_count() template <class KeySaver> @@ -2017,10 +2017,10 @@ inline bool operator==(const THashMultiMap<Key, T, HF, EqKey, Alloc>& hm1, const return true; } -template <class Key, class T, class HF, class EqKey, class Alloc> +template <class Key, class T, class HF, class EqKey, class Alloc> inline bool operator!=(const THashMultiMap<Key, T, HF, EqKey, Alloc>& hm1, const THashMultiMap<Key, T, HF, EqKey, Alloc>& hm2) { - return !(hm1 == hm2); -} + return !(hm1 == hm2); +} // Cannot name it just 'Hash' because it clashes with too many class members in the code. template <class T> |