|
@@ -0,0 +1,1429 @@
|
|
|
+#pragma once
|
|
|
+
|
|
|
+#include "fwd.h"
|
|
|
+#include "mapfindptr.h"
|
|
|
+
|
|
|
+#include <util/memory/alloc.h>
|
|
|
+#include <util/system/compiler.h>
|
|
|
+#include <util/system/type_name.h>
|
|
|
+#include <util/system/yassert.h>
|
|
|
+#include <util/str_stl.h>
|
|
|
+#include "yexception.h"
|
|
|
+#include "typetraits.h"
|
|
|
+#include "utility.h"
|
|
|
+
|
|
|
+#include <algorithm>
|
|
|
+#include <initializer_list>
|
|
|
+#include <memory>
|
|
|
+#include <tuple>
|
|
|
+#include <utility>
|
|
|
+
|
|
|
+#include <cstdlib>
|
|
|
+
|
|
|
+#include "hash_primes.h"
|
|
|
+
|
|
|
+struct TSelect1st {
|
|
|
+ template <class TPair>
|
|
|
+ inline const typename TPair::first_type& operator()(const TPair& x) const {
|
|
|
+ return x.first;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+template <class Value>
|
|
|
+struct __yhashtable_node {
|
|
|
+ /** If the first bit is not set, then this is a pointer to the next node in
|
|
|
+ * the list of nodes for the current bucket. Otherwise this is a pointer of
|
|
|
+ * type __yhashtable_node**, pointing back into the buckets array.
|
|
|
+ *
|
|
|
+ * 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;
|
|
|
+};
|
|
|
+
|
|
|
+template <class Value, class Key, class HashFcn,
|
|
|
+ class ExtractKey, class EqualKey, class Alloc>
|
|
|
+class THashTable;
|
|
|
+
|
|
|
+template <class Key, class T, class HashFcn,
|
|
|
+ class EqualKey, typename size_type_f>
|
|
|
+class sthash;
|
|
|
+
|
|
|
+template <class Value>
|
|
|
+struct __yhashtable_iterator;
|
|
|
+
|
|
|
+template <class Value>
|
|
|
+struct __yhashtable_const_iterator;
|
|
|
+
|
|
|
+template <class Value>
|
|
|
+struct __yhashtable_iterator {
|
|
|
+ using iterator = __yhashtable_iterator<Value>;
|
|
|
+ using const_iterator = __yhashtable_const_iterator<Value>;
|
|
|
+ using node = __yhashtable_node<Value>;
|
|
|
+
|
|
|
+ using iterator_category = std::forward_iterator_tag;
|
|
|
+ using value_type = Value;
|
|
|
+ using difference_type = ptrdiff_t;
|
|
|
+ using size_type = size_t;
|
|
|
+ using reference = Value&;
|
|
|
+ using pointer = Value*;
|
|
|
+
|
|
|
+ node* cur;
|
|
|
+
|
|
|
+ explicit __yhashtable_iterator(node* n)
|
|
|
+ : cur(n)
|
|
|
+ {
|
|
|
+ } /*y*/
|
|
|
+ __yhashtable_iterator() = default;
|
|
|
+
|
|
|
+ reference operator*() const {
|
|
|
+ return cur->val;
|
|
|
+ }
|
|
|
+ pointer operator->() const {
|
|
|
+ return &(operator*());
|
|
|
+ }
|
|
|
+ iterator& operator++();
|
|
|
+ iterator operator++(int);
|
|
|
+ bool operator==(const iterator& it) const {
|
|
|
+ return cur == it.cur;
|
|
|
+ }
|
|
|
+ bool operator!=(const iterator& it) const {
|
|
|
+ return cur != it.cur;
|
|
|
+ }
|
|
|
+ bool IsEnd() const {
|
|
|
+ return !cur;
|
|
|
+ }
|
|
|
+ Y_FORCE_INLINE explicit operator bool() const noexcept {
|
|
|
+ return cur != nullptr;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+template <class Value>
|
|
|
+struct __yhashtable_const_iterator {
|
|
|
+ using iterator = __yhashtable_iterator<Value>;
|
|
|
+ using const_iterator = __yhashtable_const_iterator<Value>;
|
|
|
+ using node = __yhashtable_node<Value>;
|
|
|
+
|
|
|
+ using iterator_category = std::forward_iterator_tag;
|
|
|
+ using value_type = Value;
|
|
|
+ using difference_type = ptrdiff_t;
|
|
|
+ using size_type = size_t;
|
|
|
+ using reference = const Value&;
|
|
|
+ using pointer = const Value*;
|
|
|
+
|
|
|
+ const node* cur;
|
|
|
+
|
|
|
+ explicit __yhashtable_const_iterator(const node* n)
|
|
|
+ : cur(n)
|
|
|
+ {
|
|
|
+ }
|
|
|
+ __yhashtable_const_iterator() {
|
|
|
+ }
|
|
|
+ __yhashtable_const_iterator(const iterator& it)
|
|
|
+ : cur(it.cur)
|
|
|
+ {
|
|
|
+ }
|
|
|
+ reference operator*() const {
|
|
|
+ return cur->val;
|
|
|
+ }
|
|
|
+ pointer operator->() const {
|
|
|
+ return &(operator*());
|
|
|
+ }
|
|
|
+ const_iterator& operator++();
|
|
|
+ const_iterator operator++(int);
|
|
|
+ bool operator==(const const_iterator& it) const {
|
|
|
+ return cur == it.cur;
|
|
|
+ }
|
|
|
+ bool operator!=(const const_iterator& it) const {
|
|
|
+ return cur != it.cur;
|
|
|
+ }
|
|
|
+ bool IsEnd() const {
|
|
|
+ return !cur;
|
|
|
+ }
|
|
|
+ Y_FORCE_INLINE explicit operator bool() const noexcept {
|
|
|
+ return cur != nullptr;
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+/**
|
|
|
+ * 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:
|
|
|
+ _allocator_base(const Alloc& other)
|
|
|
+ : Alloc(other)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ Alloc& _get_alloc() {
|
|
|
+ return static_cast<Alloc&>(*this);
|
|
|
+ }
|
|
|
+ const Alloc& _get_alloc() const {
|
|
|
+ return static_cast<const Alloc&>(*this);
|
|
|
+ }
|
|
|
+ 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.
|
|
|
+ * - 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.
|
|
|
+ *
|
|
|
+ * `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.
|
|
|
+ */
|
|
|
+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:
|
|
|
+ using allocator_type = Alloc;
|
|
|
+ using value_type = T;
|
|
|
+ using pointer = T*;
|
|
|
+ using const_pointer = const T*;
|
|
|
+ using reference = T&;
|
|
|
+ using const_reference = const T&;
|
|
|
+ using iterator = pointer;
|
|
|
+ using const_iterator = const_pointer;
|
|
|
+ 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;
|
|
|
+ }
|
|
|
+ const_iterator begin() const {
|
|
|
+ return Data;
|
|
|
+ }
|
|
|
+ iterator end() {
|
|
|
+ return Data + Size();
|
|
|
+ }
|
|
|
+ const_iterator end() const {
|
|
|
+ return Data + Size();
|
|
|
+ }
|
|
|
+
|
|
|
+ pointer data() {
|
|
|
+ return Data;
|
|
|
+ }
|
|
|
+ const_pointer data() const {
|
|
|
+ return Data;
|
|
|
+ }
|
|
|
+
|
|
|
+ size_type size() const {
|
|
|
+ return Size();
|
|
|
+ }
|
|
|
+ size_type capacity() const {
|
|
|
+ return *reinterpret_cast<size_type*>(Data - 1);
|
|
|
+ }
|
|
|
+ TBucketDivisor ExtSize() const {
|
|
|
+ return Size;
|
|
|
+ }
|
|
|
+ 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:
|
|
|
+ /** 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.
|
|
|
+ */
|
|
|
+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:
|
|
|
+ _yhashtable_base(const HashFcn& hash, const ExtractKey& extract, const EqualKey& equals, const Alloc& alloc)
|
|
|
+ : base_type(alloc)
|
|
|
+ , hash_(hash)
|
|
|
+ , extract_(extract)
|
|
|
+ , equals_(equals)
|
|
|
+ {
|
|
|
+ }
|
|
|
+
|
|
|
+ const EqualKey& _get_key_eq() const {
|
|
|
+ return equals_;
|
|
|
+ }
|
|
|
+ EqualKey& _get_key_eq() {
|
|
|
+ return equals_;
|
|
|
+ }
|
|
|
+ void _set_key_eq(const EqualKey& equals) {
|
|
|
+ this->equals_ = equals;
|
|
|
+ }
|
|
|
+
|
|
|
+ const ExtractKey& _get_key_extract() const {
|
|
|
+ return extract_;
|
|
|
+ }
|
|
|
+ ExtractKey& _get_key_extract() {
|
|
|
+ return extract_;
|
|
|
+ }
|
|
|
+ void _set_key_extract(const ExtractKey& extract) {
|
|
|
+ this->extract_ = extract;
|
|
|
+ }
|
|
|
+
|
|
|
+ const HashFcn& _get_hash_fun() const {
|
|
|
+ return hash_;
|
|
|
+ }
|
|
|
+ HashFcn& _get_hash_fun() {
|
|
|
+ return hash_;
|
|
|
+ }
|
|
|
+ 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:
|
|
|
+ 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:
|
|
|
+ _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>
|
|
|
+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>
|
|
|
+class THashTable: private _yhashtable_traits<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>::base_type {
|
|
|
+ using traits_type = _yhashtable_traits<Value, Key, HashFcn, ExtractKey, EqualKey, Alloc>;
|
|
|
+ using base_type = typename traits_type::base_type;
|
|
|
+ using node = typename traits_type::node;
|
|
|
+ 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;
|
|
|
+ using hasher = HashFcn;
|
|
|
+ using key_equal = EqualKey;
|
|
|
+ using key_extract = ExtractKey;
|
|
|
+ using allocator_type = Alloc;
|
|
|
+ using node_allocator_type = typename traits_type::node_allocator_type;
|
|
|
+
|
|
|
+ using size_type = size_t;
|
|
|
+ using difference_type = ptrdiff_t;
|
|
|
+ using pointer = value_type*;
|
|
|
+ using const_pointer = const value_type*;
|
|
|
+ using reference = value_type&;
|
|
|
+ using const_reference = const value_type&;
|
|
|
+
|
|
|
+ node_allocator_type& GetNodeAllocator() {
|
|
|
+ return this->_get_alloc();
|
|
|
+ }
|
|
|
+ const node_allocator_type& GetNodeAllocator() const {
|
|
|
+ return this->_get_alloc();
|
|
|
+ }
|
|
|
+ key_equal key_eq() const {
|
|
|
+ return this->_get_key_eq();
|
|
|
+ }
|
|
|
+ hasher hash_function() const {
|
|
|
+ return this->_get_hash_fun();
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ template <class KeyL, class KeyR>
|
|
|
+ bool equals(const KeyL& l, const KeyR& r) const {
|
|
|
+ return this->_get_key_eq()(l, r);
|
|
|
+ }
|
|
|
+
|
|
|
+ /* This method is templated to postpone instantiation of key extraction functor. */
|
|
|
+ template <class ValueL>
|
|
|
+ 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);
|
|
|
+ Y_ASSERT((reinterpret_cast<uintptr_t>(result) & 1) == 0); /* We're using the last bit of the node pointer. */
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+ void put_node(node* p) {
|
|
|
+ this->_get_alloc().deallocate(p, 1);
|
|
|
+ }
|
|
|
+
|
|
|
+ buckets_type buckets;
|
|
|
+ size_type num_elements;
|
|
|
+
|
|
|
+public:
|
|
|
+ using iterator = __yhashtable_iterator<Value>;
|
|
|
+ using const_iterator = __yhashtable_const_iterator<Value>;
|
|
|
+ using insert_ctx = node**;
|
|
|
+
|
|
|
+ friend struct __yhashtable_iterator<Value>;
|
|
|
+ friend struct __yhashtable_const_iterator<Value>;
|
|
|
+
|
|
|
+public:
|
|
|
+ THashTable()
|
|
|
+ : base_type(HashFcn(), ExtractKey(), EqualKey(), node_allocator_type())
|
|
|
+ , buckets(nodep_allocator_type())
|
|
|
+ , num_elements(0)
|
|
|
+ {
|
|
|
+ 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())
|
|
|
+ , num_elements(0)
|
|
|
+ {
|
|
|
+ initialize_buckets(buckets, n);
|
|
|
+ }
|
|
|
+
|
|
|
+ THashTable(size_type n, const HashFcn& hf, const EqualKey& eql)
|
|
|
+ : base_type(hf, ExtractKey(), eql, node_allocator_type())
|
|
|
+ , buckets(nodep_allocator_type())
|
|
|
+ , num_elements(0)
|
|
|
+ {
|
|
|
+ initialize_buckets(buckets, n);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class TAllocParam>
|
|
|
+ THashTable(size_type n, const HashFcn& hf, const EqualKey& eql, TAllocParam* allocParam)
|
|
|
+ : base_type(hf, ExtractKey(), eql, allocParam)
|
|
|
+ , buckets(allocParam)
|
|
|
+ , 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())
|
|
|
+ , buckets(ht.buckets.get_allocator())
|
|
|
+ , num_elements(0)
|
|
|
+ {
|
|
|
+ if (ht.empty()) {
|
|
|
+ initialize_buckets(buckets, 0);
|
|
|
+ } 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())
|
|
|
+ , num_elements(0)
|
|
|
+ {
|
|
|
+ initialize_buckets(buckets, 0);
|
|
|
+ this->swap(ht);
|
|
|
+ }
|
|
|
+
|
|
|
+ THashTable& operator=(const THashTable& ht) {
|
|
|
+ if (&ht != this) {
|
|
|
+ basic_clear();
|
|
|
+ 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 {
|
|
|
+ if (buckets.capacity() > ht.buckets.size()) {
|
|
|
+ buckets.resize_noallocate(ht.buckets.ExtSize());
|
|
|
+ } else {
|
|
|
+ deinitialize_buckets(buckets);
|
|
|
+ initialize_buckets_dynamic(buckets, ht.buckets.ExtSize());
|
|
|
+ }
|
|
|
+
|
|
|
+ copy_from_dynamic(ht);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return *this;
|
|
|
+ }
|
|
|
+
|
|
|
+ THashTable& operator=(THashTable&& ht) noexcept {
|
|
|
+ basic_clear();
|
|
|
+ swap(ht);
|
|
|
+
|
|
|
+ return *this;
|
|
|
+ }
|
|
|
+
|
|
|
+ ~THashTable() {
|
|
|
+ basic_clear();
|
|
|
+ deinitialize_buckets(buckets);
|
|
|
+ }
|
|
|
+
|
|
|
+ size_type size() const noexcept {
|
|
|
+ return num_elements;
|
|
|
+ }
|
|
|
+ size_type max_size() const noexcept {
|
|
|
+ return size_type(-1);
|
|
|
+ }
|
|
|
+
|
|
|
+ Y_PURE_FUNCTION bool empty() const noexcept {
|
|
|
+ return size() == 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ void swap(THashTable& ht) {
|
|
|
+ base_type::swap(ht);
|
|
|
+ buckets.swap(ht.buckets);
|
|
|
+ DoSwap(num_elements, ht.num_elements);
|
|
|
+ }
|
|
|
+
|
|
|
+ iterator begin() {
|
|
|
+ for (size_type n = 0; n < buckets.size(); ++n) /*y*/
|
|
|
+ if (buckets[n])
|
|
|
+ return iterator(buckets[n]); /*y*/
|
|
|
+ return end();
|
|
|
+ }
|
|
|
+
|
|
|
+ iterator end() {
|
|
|
+ return iterator(nullptr);
|
|
|
+ } /*y*/
|
|
|
+
|
|
|
+ const_iterator begin() const {
|
|
|
+ for (size_type n = 0; n < buckets.size(); ++n) /*y*/
|
|
|
+ if (buckets[n])
|
|
|
+ return const_iterator(buckets[n]); /*y*/
|
|
|
+ return end();
|
|
|
+ }
|
|
|
+
|
|
|
+ const_iterator end() const {
|
|
|
+ return const_iterator(nullptr);
|
|
|
+ } /*y*/
|
|
|
+
|
|
|
+public:
|
|
|
+ size_type bucket_count() const {
|
|
|
+ return buckets.size();
|
|
|
+ } /*y*/
|
|
|
+
|
|
|
+ size_type bucket_size(size_type bucket) const {
|
|
|
+ size_type result = 0;
|
|
|
+ if (const node* cur = buckets[bucket])
|
|
|
+ for (; !((uintptr_t)cur & 1); cur = cur->next)
|
|
|
+ result += 1;
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class OtherValue>
|
|
|
+ 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) {
|
|
|
+ reserve(num_elements + 1);
|
|
|
+ return emplace_equal_noresize(obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename... Args>
|
|
|
+ iterator emplace_equal(Args&&... args) {
|
|
|
+ reserve(num_elements + 1);
|
|
|
+ return emplace_equal_noresize(std::forward<Args>(args)...);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class OtherValue>
|
|
|
+ iterator insert_direct(const OtherValue& obj, insert_ctx ins) {
|
|
|
+ return emplace_direct(ins, obj);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename... Args>
|
|
|
+ iterator emplace_direct(insert_ctx ins, Args&&... args) {
|
|
|
+ bool resized = reserve(num_elements + 1);
|
|
|
+ node* tmp = new_node(std::forward<Args>(args)...);
|
|
|
+ if (resized) {
|
|
|
+ find_i(get_key(tmp->val), ins);
|
|
|
+ }
|
|
|
+ tmp->next = *ins ? *ins : (node*)((uintptr_t)(ins + 1) | 1);
|
|
|
+ *ins = tmp;
|
|
|
+ ++num_elements;
|
|
|
+ return iterator(tmp);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename... Args>
|
|
|
+ std::pair<iterator, bool> emplace_unique(Args&&... args) {
|
|
|
+ reserve(num_elements + 1);
|
|
|
+ return emplace_unique_noresize(std::forward<Args>(args)...);
|
|
|
+ }
|
|
|
+
|
|
|
+ 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 <typename... Args>
|
|
|
+ iterator emplace_equal_noresize(Args&&... args);
|
|
|
+
|
|
|
+ template <class InputIterator>
|
|
|
+ void insert_unique(InputIterator f, InputIterator l) {
|
|
|
+ insert_unique(f, l, typename std::iterator_traits<InputIterator>::iterator_category());
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class InputIterator>
|
|
|
+ void insert_equal(InputIterator f, InputIterator l) {
|
|
|
+ insert_equal(f, l, typename std::iterator_traits<InputIterator>::iterator_category());
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class InputIterator>
|
|
|
+ void insert_unique(InputIterator f, InputIterator l, std::input_iterator_tag) {
|
|
|
+ for (; f != l; ++f)
|
|
|
+ insert_unique(*f);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class InputIterator>
|
|
|
+ void insert_equal(InputIterator f, InputIterator l, std::input_iterator_tag) {
|
|
|
+ for (; f != l; ++f)
|
|
|
+ insert_equal(*f);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class ForwardIterator>
|
|
|
+ void insert_unique(ForwardIterator f, ForwardIterator l, std::forward_iterator_tag) {
|
|
|
+ difference_type n = std::distance(f, l);
|
|
|
+
|
|
|
+ reserve(num_elements + n);
|
|
|
+ for (; n > 0; --n, ++f)
|
|
|
+ insert_unique_noresize(*f);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class ForwardIterator>
|
|
|
+ void insert_equal(ForwardIterator f, ForwardIterator l, std::forward_iterator_tag) {
|
|
|
+ difference_type n = std::distance(f, l);
|
|
|
+
|
|
|
+ reserve(num_elements + n);
|
|
|
+ for (; n > 0; --n, ++f)
|
|
|
+ emplace_equal_noresize(*f);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class OtherValue>
|
|
|
+ reference find_or_insert(const OtherValue& v);
|
|
|
+
|
|
|
+ template <class OtherKey>
|
|
|
+ iterator find(const OtherKey& key) {
|
|
|
+ size_type n = bkt_num_key(key);
|
|
|
+ node* first;
|
|
|
+ for (first = buckets[n];
|
|
|
+ first && !equals(get_key(first->val), key);
|
|
|
+ first = ((uintptr_t)first->next & 1) ? nullptr : first->next) /*y*/
|
|
|
+ {
|
|
|
+ }
|
|
|
+ return iterator(first); /*y*/
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class OtherKey>
|
|
|
+ const_iterator find(const OtherKey& key) const {
|
|
|
+ size_type n = bkt_num_key(key);
|
|
|
+ const node* first;
|
|
|
+ for (first = buckets[n];
|
|
|
+ first && !equals(get_key(first->val), key);
|
|
|
+ first = ((uintptr_t)first->next & 1) ? nullptr : first->next) /*y*/
|
|
|
+ {
|
|
|
+ }
|
|
|
+ return const_iterator(first); /*y*/
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class OtherKey>
|
|
|
+ iterator find_i(const OtherKey& key, insert_ctx& ins);
|
|
|
+
|
|
|
+ template <class OtherKey>
|
|
|
+ size_type count(const OtherKey& key) const {
|
|
|
+ const size_type n = bkt_num_key(key);
|
|
|
+ size_type result = 0;
|
|
|
+
|
|
|
+ if (const node* cur = buckets[n])
|
|
|
+ for (; !((uintptr_t)cur & 1); cur = cur->next)
|
|
|
+ if (equals(get_key(cur->val), key))
|
|
|
+ ++result;
|
|
|
+ return result;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class OtherKey>
|
|
|
+ std::pair<iterator, iterator> equal_range(const OtherKey& key);
|
|
|
+
|
|
|
+ template <class OtherKey>
|
|
|
+ std::pair<const_iterator, const_iterator> equal_range(const OtherKey& key) const;
|
|
|
+
|
|
|
+ template <class OtherKey>
|
|
|
+ size_type erase(const OtherKey& key);
|
|
|
+
|
|
|
+ template <class OtherKey>
|
|
|
+ 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);
|
|
|
+
|
|
|
+ void erase(const const_iterator& it);
|
|
|
+ void erase(const_iterator first, const_iterator last);
|
|
|
+
|
|
|
+ bool reserve(size_type num_elements_hint);
|
|
|
+ Y_REINITIALIZES_OBJECT 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;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 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;
|
|
|
+
|
|
|
+ Y_REINITIALIZES_OBJECT void clear(size_type downsize) {
|
|
|
+ basic_clear();
|
|
|
+
|
|
|
+ if (downsize < buckets.size()) {
|
|
|
+ const TBucketDivisor newSize = HashBucketCountExt(downsize);
|
|
|
+ if (newSize() < buckets.size()) {
|
|
|
+ Y_ASSERT(newSize() >= 7); /* We cannot downsize static buckets. */
|
|
|
+ buckets.resize_noallocate(newSize);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Clears the hashtable and tries to reasonably downsize it. Note that
|
|
|
+ * downsizing is mainly for the following use case:
|
|
|
+ *
|
|
|
+ * THashTable hash;
|
|
|
+ * for(...) {
|
|
|
+ * if (someCond())
|
|
|
+ * hash.clear();
|
|
|
+ * hash.insert(...);
|
|
|
+ * }
|
|
|
+ *
|
|
|
+ * Here if at some point `hash` gets really big, then all the following calls
|
|
|
+ * to `clear` become really slow as they have to iterate through all the the
|
|
|
+ * empty buckets. This is worked around by squeezing the buckets array a little
|
|
|
+ * bit with every `clear` call.
|
|
|
+ *
|
|
|
+ * Alternatively, the user can call `basic_clear`, which doesn't do the
|
|
|
+ * downsizing.
|
|
|
+ */
|
|
|
+ Y_REINITIALIZES_OBJECT void clear() {
|
|
|
+ if (num_elements)
|
|
|
+ clear((num_elements * 2 + buckets.size()) / 3);
|
|
|
+ }
|
|
|
+
|
|
|
+private:
|
|
|
+ static void initialize_buckets(buckets_type& buckets, size_type sizeHint) {
|
|
|
+ if (sizeHint == 0) {
|
|
|
+ buckets.initialize_static(reinterpret_cast<node**>(const_cast<void**>(_yhashtable_empty_data)) + 1, TBucketDivisor::One());
|
|
|
+ } 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 {
|
|
|
+ return bkt_num_key(key, buckets.ExtSize());
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class OtherValue>
|
|
|
+ size_type bkt_num(const OtherValue& obj) const {
|
|
|
+ return bkt_num_key(get_key(obj));
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class OtherKey>
|
|
|
+ size_type bkt_num_key(const OtherKey& key, TBucketDivisor n) const {
|
|
|
+ const size_type bucket = n.Remainder(this->_get_hash_fun()(key));
|
|
|
+ Y_ASSERT((0 <= bucket) && (bucket < n()));
|
|
|
+ return bucket;
|
|
|
+ }
|
|
|
+
|
|
|
+ template <class OtherValue>
|
|
|
+ size_type bkt_num(const OtherValue& obj, TBucketDivisor n) const {
|
|
|
+ return bkt_num_key(get_key(obj), n);
|
|
|
+ }
|
|
|
+
|
|
|
+ template <typename... Args>
|
|
|
+ node* new_node(Args&&... val) {
|
|
|
+ node* n = get_node();
|
|
|
+ n->next = (node*)1; /*y*/ // just for a case
|
|
|
+ try {
|
|
|
+ new (static_cast<void*>(&n->val)) Value(std::forward<Args>(val)...);
|
|
|
+ } catch (...) {
|
|
|
+ put_node(n);
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+ return n;
|
|
|
+ }
|
|
|
+
|
|
|
+ void delete_node(node* n) {
|
|
|
+ n->val.~Value();
|
|
|
+ // n->next = (node*) 0xDeadBeeful;
|
|
|
+ put_node(n);
|
|
|
+ }
|
|
|
+
|
|
|
+ void erase_bucket(const size_type n, node* first, node* last);
|
|
|
+ void erase_bucket(const size_type n, node* last);
|
|
|
+
|
|
|
+ void copy_from_dynamic(const THashTable& ht);
|
|
|
+};
|
|
|
+
|
|
|
+template <class V>
|
|
|
+__yhashtable_iterator<V>& __yhashtable_iterator<V>::operator++() {
|
|
|
+ Y_ASSERT(cur);
|
|
|
+ cur = cur->next;
|
|
|
+ if ((uintptr_t)cur & 1) {
|
|
|
+ node** bucket = (node**)((uintptr_t)cur & ~1);
|
|
|
+ while (*bucket == nullptr)
|
|
|
+ ++bucket;
|
|
|
+ Y_ASSERT(*bucket != nullptr);
|
|
|
+ cur = (node*)((uintptr_t)*bucket & ~1);
|
|
|
+ }
|
|
|
+ return *this;
|
|
|
+}
|
|
|
+
|
|
|
+template <class V>
|
|
|
+inline __yhashtable_iterator<V> __yhashtable_iterator<V>::operator++(int) {
|
|
|
+ iterator tmp = *this;
|
|
|
+ ++*this;
|
|
|
+ return tmp;
|
|
|
+}
|
|
|
+
|
|
|
+template <class V>
|
|
|
+__yhashtable_const_iterator<V>& __yhashtable_const_iterator<V>::operator++() {
|
|
|
+ Y_ASSERT(cur);
|
|
|
+ cur = cur->next;
|
|
|
+ if ((uintptr_t)cur & 1) {
|
|
|
+ node** bucket = (node**)((uintptr_t)cur & ~1);
|
|
|
+ while (*bucket == nullptr)
|
|
|
+ ++bucket;
|
|
|
+ Y_ASSERT(*bucket != nullptr);
|
|
|
+ cur = (node*)((uintptr_t)*bucket & ~1);
|
|
|
+ }
|
|
|
+ return *this;
|
|
|
+}
|
|
|
+
|
|
|
+template <class V>
|
|
|
+inline __yhashtable_const_iterator<V> __yhashtable_const_iterator<V>::operator++(int) {
|
|
|
+ const_iterator tmp = *this;
|
|
|
+ ++*this;
|
|
|
+ return tmp;
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+template <typename... Args>
|
|
|
+std::pair<typename THashTable<V, K, HF, Ex, Eq, A>::iterator, bool> THashTable<V, K, HF, Ex, Eq, A>::emplace_unique_noresize(Args&&... args) {
|
|
|
+ auto deleter = [&](node* tmp) { delete_node(tmp); };
|
|
|
+ node* tmp = new_node(std::forward<Args>(args)...);
|
|
|
+ std::unique_ptr<node, decltype(deleter)> guard(tmp, deleter);
|
|
|
+
|
|
|
+ const size_type n = bkt_num(tmp->val);
|
|
|
+ node* first = buckets[n];
|
|
|
+
|
|
|
+ if (first) /*y*/
|
|
|
+ for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/
|
|
|
+ if (equals(get_key(cur->val), get_key(tmp->val)))
|
|
|
+ return std::pair<iterator, bool>(iterator(cur), false); /*y*/
|
|
|
+
|
|
|
+ guard.release();
|
|
|
+ tmp->next = first ? first : (node*)((uintptr_t)&buckets[n + 1] | 1); /*y*/
|
|
|
+ buckets[n] = tmp;
|
|
|
+ ++num_elements;
|
|
|
+ return std::pair<iterator, bool>(iterator(tmp), true); /*y*/
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+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];
|
|
|
+
|
|
|
+ if (first) /*y*/
|
|
|
+ for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/
|
|
|
+ if (equals(get_key(cur->val), get_key(obj)))
|
|
|
+ return std::pair<iterator, bool>(iterator(cur), false); /*y*/
|
|
|
+
|
|
|
+ node* tmp = new_node(obj);
|
|
|
+ tmp->next = first ? first : (node*)((uintptr_t)&buckets[n + 1] | 1); /*y*/
|
|
|
+ buckets[n] = tmp;
|
|
|
+ ++num_elements;
|
|
|
+ return std::pair<iterator, bool>(iterator(tmp), true); /*y*/
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+template <typename... Args>
|
|
|
+__yhashtable_iterator<V> THashTable<V, K, HF, Ex, Eq, A>::emplace_equal_noresize(Args&&... args) {
|
|
|
+ auto deleter = [&](node* tmp) { delete_node(tmp); };
|
|
|
+ node* tmp = new_node(std::forward<Args>(args)...);
|
|
|
+ std::unique_ptr<node, decltype(deleter)> guard(tmp, deleter);
|
|
|
+ const size_type n = bkt_num(tmp->val);
|
|
|
+ node* first = buckets[n];
|
|
|
+
|
|
|
+ if (first) /*y*/
|
|
|
+ for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/
|
|
|
+ if (equals(get_key(cur->val), get_key(tmp->val))) {
|
|
|
+ guard.release();
|
|
|
+ tmp->next = cur->next;
|
|
|
+ cur->next = tmp;
|
|
|
+ ++num_elements;
|
|
|
+ return iterator(tmp); /*y*/
|
|
|
+ }
|
|
|
+
|
|
|
+ guard.release();
|
|
|
+ tmp->next = first ? first : (node*)((uintptr_t)&buckets[n + 1] | 1); /*y*/
|
|
|
+ buckets[n] = tmp;
|
|
|
+ ++num_elements;
|
|
|
+ return iterator(tmp); /*y*/
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+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);
|
|
|
+
|
|
|
+ size_type n = bkt_num_key(get_key(v));
|
|
|
+ node* first = buckets[n];
|
|
|
+
|
|
|
+ if (first) /*y*/
|
|
|
+ for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/
|
|
|
+ if (equals(get_key(cur->val), get_key(v)))
|
|
|
+ return cur->val;
|
|
|
+
|
|
|
+ node* tmp = new_node(v);
|
|
|
+ tmp->next = first ? first : (node*)((uintptr_t)&buckets[n + 1] | 1); /*y*/
|
|
|
+ buckets[n] = tmp;
|
|
|
+ ++num_elements;
|
|
|
+ return tmp->val;
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+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];
|
|
|
+ node* first = buckets[n];
|
|
|
+
|
|
|
+ if (first) /*y*/
|
|
|
+ for (node* cur = first; !((uintptr_t)cur & 1); cur = cur->next) /*y*/
|
|
|
+ if (equals(get_key(cur->val), key))
|
|
|
+ return iterator(cur); /*y*/
|
|
|
+ return end();
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+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);
|
|
|
+ node* first = buckets[n];
|
|
|
+
|
|
|
+ if (first) /*y*/
|
|
|
+ for (; !((uintptr_t)first & 1); first = first->next) { /*y*/
|
|
|
+ if (equals(get_key(first->val), key)) {
|
|
|
+ for (node* cur = first->next; !((uintptr_t)cur & 1); cur = cur->next)
|
|
|
+ if (!equals(get_key(cur->val), key))
|
|
|
+ return pii(iterator(first), iterator(cur)); /*y*/
|
|
|
+ for (size_type m = n + 1; m < buckets.size(); ++m) /*y*/
|
|
|
+ if (buckets[m])
|
|
|
+ return pii(iterator(first), /*y*/
|
|
|
+ iterator(buckets[m])); /*y*/
|
|
|
+ return pii(iterator(first), end()); /*y*/
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return pii(end(), end());
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+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);
|
|
|
+ const node* first = buckets[n];
|
|
|
+
|
|
|
+ if (first) /*y*/
|
|
|
+ for (; !((uintptr_t)first & 1); first = first->next) { /*y*/
|
|
|
+ if (equals(get_key(first->val), key)) {
|
|
|
+ for (const node* cur = first->next; !((uintptr_t)cur & 1); cur = cur->next)
|
|
|
+ if (!equals(get_key(cur->val), key))
|
|
|
+ return pii(const_iterator(first), /*y*/
|
|
|
+ const_iterator(cur)); /*y*/
|
|
|
+ for (size_type m = n + 1; m < buckets.size(); ++m) /*y*/
|
|
|
+ if (buckets[m])
|
|
|
+ return pii(const_iterator(first /*y*/),
|
|
|
+ const_iterator(buckets[m] /*y*/));
|
|
|
+ return pii(const_iterator(first /*y*/), end());
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return pii(end(), end());
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+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];
|
|
|
+ size_type erased = 0;
|
|
|
+
|
|
|
+ if (first) {
|
|
|
+ node* cur = first;
|
|
|
+ node* next = cur->next;
|
|
|
+ while (!((uintptr_t)next & 1)) { /*y*/
|
|
|
+ if (equals(get_key(next->val), key)) {
|
|
|
+ cur->next = next->next;
|
|
|
+ ++erased;
|
|
|
+ --num_elements;
|
|
|
+ delete_node(next);
|
|
|
+ next = cur->next;
|
|
|
+ } else {
|
|
|
+ cur = next;
|
|
|
+ next = cur->next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (equals(get_key(first->val), key)) {
|
|
|
+ buckets[n] = ((uintptr_t)first->next & 1) ? nullptr : first->next; /*y*/
|
|
|
+ ++erased;
|
|
|
+ --num_elements;
|
|
|
+ delete_node(first);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return erased;
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+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];
|
|
|
+
|
|
|
+ if (first) {
|
|
|
+ node* cur = first;
|
|
|
+ node* next = cur->next;
|
|
|
+ while (!((uintptr_t)next & 1)) { /*y*/
|
|
|
+ if (equals(get_key(next->val), key)) {
|
|
|
+ cur->next = next->next;
|
|
|
+ --num_elements;
|
|
|
+ delete_node(next);
|
|
|
+ return 1;
|
|
|
+ } else {
|
|
|
+ cur = next;
|
|
|
+ next = cur->next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (equals(get_key(first->val), key)) {
|
|
|
+ buckets[n] = ((uintptr_t)first->next & 1) ? nullptr : first->next; /*y*/
|
|
|
+ --num_elements;
|
|
|
+ delete_node(first);
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+void THashTable<V, K, HF, Ex, Eq, A>::erase(const iterator& it) {
|
|
|
+ if (node* const p = it.cur) {
|
|
|
+ const size_type n = bkt_num(p->val);
|
|
|
+ node* cur = buckets[n];
|
|
|
+
|
|
|
+ if (cur == p) {
|
|
|
+ buckets[n] = ((uintptr_t)cur->next & 1) ? nullptr : cur->next; /*y*/
|
|
|
+ delete_node(cur);
|
|
|
+ --num_elements;
|
|
|
+ } else {
|
|
|
+ node* next = cur->next;
|
|
|
+ while (!((uintptr_t)next & 1)) {
|
|
|
+ if (next == p) {
|
|
|
+ cur->next = next->next;
|
|
|
+ delete_node(next);
|
|
|
+ --num_elements;
|
|
|
+ break;
|
|
|
+ } else {
|
|
|
+ cur = next;
|
|
|
+ next = cur->next;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+void THashTable<V, K, HF, Ex, Eq, A>::erase(iterator first, iterator last) {
|
|
|
+ size_type f_bucket = first.cur ? bkt_num(first.cur->val) : buckets.size(); /*y*/
|
|
|
+ size_type l_bucket = last.cur ? bkt_num(last.cur->val) : buckets.size(); /*y*/
|
|
|
+
|
|
|
+ if (first.cur == last.cur)
|
|
|
+ return;
|
|
|
+ else if (f_bucket == l_bucket)
|
|
|
+ erase_bucket(f_bucket, first.cur, last.cur);
|
|
|
+ else {
|
|
|
+ erase_bucket(f_bucket, first.cur, nullptr);
|
|
|
+ for (size_type n = f_bucket + 1; n < l_bucket; ++n)
|
|
|
+ erase_bucket(n, nullptr);
|
|
|
+ if (l_bucket != buckets.size()) /*y*/
|
|
|
+ erase_bucket(l_bucket, last.cur);
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+inline void
|
|
|
+THashTable<V, K, HF, Ex, Eq, A>::erase(const_iterator first, const_iterator last) {
|
|
|
+ erase(iterator(const_cast<node*>(first.cur)), iterator(const_cast<node*>(last.cur)));
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+inline void THashTable<V, K, HF, Ex, Eq, A>::erase(const const_iterator& it) {
|
|
|
+ erase(iterator(const_cast<node*>(it.cur)));
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+bool THashTable<V, K, HF, Ex, Eq, A>::reserve(size_type num_elements_hint) {
|
|
|
+ const size_type old_n = buckets.size(); /*y*/
|
|
|
+ if (num_elements_hint + 1 > old_n) {
|
|
|
+ if (old_n != 1 && num_elements_hint <= old_n) // TODO: this if is for backwards compatibility down to order-in-buckets level. Can be safely removed.
|
|
|
+ return false;
|
|
|
+
|
|
|
+ const TBucketDivisor n = HashBucketCountExt(num_elements_hint + 1, buckets.BucketDivisorHint() + 1);
|
|
|
+ if (n() > old_n) {
|
|
|
+ buckets_type tmp(buckets.get_allocator());
|
|
|
+ initialize_buckets_dynamic(tmp, n);
|
|
|
+#ifdef __STL_USE_EXCEPTIONS
|
|
|
+ try {
|
|
|
+#endif /* __STL_USE_EXCEPTIONS */
|
|
|
+ for (size_type bucket = 0; bucket < old_n; ++bucket) {
|
|
|
+ node* first = buckets[bucket];
|
|
|
+ while (first) {
|
|
|
+ size_type new_bucket = bkt_num(first->val, n);
|
|
|
+ node* next = first->next;
|
|
|
+ buckets[bucket] = ((uintptr_t)next & 1) ? nullptr : next; /*y*/
|
|
|
+ next = tmp[new_bucket];
|
|
|
+ first->next = next ? next : (node*)((uintptr_t) & (tmp[new_bucket + 1]) | 1); /*y*/
|
|
|
+ tmp[new_bucket] = first;
|
|
|
+ first = buckets[bucket];
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ buckets.swap(tmp);
|
|
|
+ deinitialize_buckets(tmp);
|
|
|
+
|
|
|
+ return true;
|
|
|
+#ifdef __STL_USE_EXCEPTIONS
|
|
|
+ } catch (...) {
|
|
|
+ for (size_type bucket = 0; bucket < tmp.size() - 1; ++bucket) {
|
|
|
+ while (tmp[bucket]) {
|
|
|
+ node* next = tmp[bucket]->next;
|
|
|
+ delete_node(tmp[bucket]);
|
|
|
+ tmp[bucket] = ((uintptr_t)next & 1) ? nullptr : next /*y*/;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+#endif /* __STL_USE_EXCEPTIONS */
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+void THashTable<V, K, HF, Ex, Eq, A>::erase_bucket(const size_type n, node* first, node* last) {
|
|
|
+ node* cur = buckets[n];
|
|
|
+ if (cur == first)
|
|
|
+ erase_bucket(n, last);
|
|
|
+ else {
|
|
|
+ node* next;
|
|
|
+ for (next = cur->next; next != first; cur = next, next = cur->next)
|
|
|
+ ;
|
|
|
+ while (next != last) { /*y; 3.1*/
|
|
|
+ cur->next = next->next;
|
|
|
+ delete_node(next);
|
|
|
+ next = ((uintptr_t)cur->next & 1) ? nullptr : cur->next; /*y*/
|
|
|
+ --num_elements;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+void THashTable<V, K, HF, Ex, Eq, A>::erase_bucket(const size_type n, node* last) {
|
|
|
+ node* cur = buckets[n];
|
|
|
+ while (cur != last) {
|
|
|
+ node* next = cur->next;
|
|
|
+ delete_node(cur);
|
|
|
+ cur = ((uintptr_t)next & 1) ? nullptr : next; /*y*/
|
|
|
+ buckets[n] = cur;
|
|
|
+ --num_elements;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+template <class V, class K, class HF, class Ex, class Eq, class A>
|
|
|
+void THashTable<V, K, HF, Ex, Eq, A>::basic_clear() {
|
|
|
+ if (!num_elements) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ node** first = buckets.begin();
|
|
|
+ node** last = buckets.end();
|
|
|
+ for (; first < last; ++first) {
|
|
|
+ node* cur = *first;
|
|
|
+ if (cur) { /*y*/
|
|
|
+ while (!((uintptr_t)cur & 1)) { /*y*/
|
|
|
+ node* next = cur->next;
|
|
|
+ delete_node(cur);
|
|
|
+ cur = next;
|
|
|
+ }
|
|
|
+ *first = nullptr;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ num_elements = 0;
|
|
|
+}
|
|
|
+
|
|
|
+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 */
|
|
|
+ for (size_type i = 0; i < ht.buckets.size(); ++i) { /*y*/
|
|
|
+ if (const node* cur = ht.buckets[i]) {
|
|
|
+ node* copy = new_node(cur->val);
|
|
|
+ buckets[i] = copy;
|
|
|
+
|
|
|
+ for (node* next = cur->next; !((uintptr_t)next & 1); cur = next, next = cur->next) {
|
|
|
+ copy->next = new_node(next->val);
|
|
|
+ copy = copy->next;
|
|
|
+ }
|
|
|
+ copy->next = (node*)((uintptr_t)&buckets[i + 1] | 1); /*y*/
|
|
|
+ }
|
|
|
+ }
|
|
|
+ num_elements = ht.num_elements;
|
|
|
+#ifdef __STL_USE_EXCEPTIONS
|
|
|
+ } catch (...) {
|
|
|
+ basic_clear();
|
|
|
+ throw;
|
|
|
+ }
|
|
|
+#endif /* __STL_USE_EXCEPTIONS */
|
|
|
+}
|
|
|
+
|
|
|
+namespace NPrivate {
|
|
|
+ template <class Key>
|
|
|
+ inline TString MapKeyToString(const Key&) {
|
|
|
+ return TypeName<Key>();
|
|
|
+ }
|
|
|
+
|
|
|
+ TString MapKeyToString(TStringBuf key);
|
|
|
+ TString MapKeyToString(unsigned short key);
|
|
|
+ TString MapKeyToString(short key);
|
|
|
+ TString MapKeyToString(unsigned int key);
|
|
|
+ TString MapKeyToString(int key);
|
|
|
+ TString MapKeyToString(unsigned long key);
|
|
|
+ TString MapKeyToString(long key);
|
|
|
+ TString MapKeyToString(unsigned long long key);
|
|
|
+ TString MapKeyToString(long long key);
|
|
|
+
|
|
|
+ inline TString MapKeyToString(const TString& key) {
|
|
|
+ return MapKeyToString(TStringBuf(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ inline TString MapKeyToString(const char* key) {
|
|
|
+ return MapKeyToString(TStringBuf(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ inline TString MapKeyToString(char* key) {
|
|
|
+ return MapKeyToString(TStringBuf(key));
|
|
|
+ }
|
|
|
+
|
|
|
+ [[noreturn]] void ThrowKeyNotFoundInHashTableException(const TStringBuf keyRepresentation);
|
|
|
+}
|
|
|
+
|
|
|
+// Cannot name it just 'Hash' because it clashes with too many class members in the code.
|
|
|
+template <class T>
|
|
|
+size_t ComputeHash(const T& value) {
|
|
|
+ return THash<T>{}(value);
|
|
|
+}
|