1322 lines
47 KiB
C
1322 lines
47 KiB
C
|
/***
|
||
|
* ==++==
|
||
|
*
|
||
|
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
*
|
||
|
* ==--==
|
||
|
* =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
|
||
|
*
|
||
|
* internal_concurrent_hash.h
|
||
|
*
|
||
|
* =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
|
||
|
****/
|
||
|
#pragma once
|
||
|
|
||
|
#include <utility>
|
||
|
#include "internal_split_ordered_list.h"
|
||
|
#include <concrt.h>
|
||
|
|
||
|
#pragma pack(push,_CRT_PACKING)
|
||
|
|
||
|
namespace Concurrency
|
||
|
{
|
||
|
namespace details
|
||
|
{
|
||
|
// Template class for hash compare
|
||
|
template<typename _Key_type, typename _Hasher, typename _Key_equality>
|
||
|
class _Hash_compare
|
||
|
{
|
||
|
public:
|
||
|
typedef _Hasher hasher;
|
||
|
|
||
|
_Hash_compare()
|
||
|
{
|
||
|
}
|
||
|
|
||
|
_Hash_compare(hasher _Hasharg) : _M_hash_object(_Hasharg)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
_Hash_compare(hasher _Hasharg, _Key_equality _Keyeqarg) : _M_hash_object(_Hasharg), _M_key_compare_object(_Keyeqarg)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
size_t operator()(const _Key_type& _Keyval) const
|
||
|
{
|
||
|
return ((size_t)_M_hash_object(_Keyval));
|
||
|
}
|
||
|
|
||
|
bool operator()(const _Key_type& _Keyval1, const _Key_type& _Keyval2) const
|
||
|
{
|
||
|
return (!_M_key_compare_object(_Keyval1, _Keyval2));
|
||
|
}
|
||
|
|
||
|
hasher _M_hash_object; // The hash object
|
||
|
_Key_equality _M_key_compare_object; // The equality comparator object
|
||
|
};
|
||
|
|
||
|
// An efficient implementation of the _Reverse function utilizes a 2^8 or 2^16 lookup table holding the
|
||
|
// bit-reversed values of [0..2^8 - 1] or [0..2^16 - 1] respectively. Those values can also be computed
|
||
|
// on the fly at a slightly higher cost.
|
||
|
extern _CRTIMP2 const unsigned char _Byte_reverse_table[];
|
||
|
|
||
|
// Given a byte, reverses it
|
||
|
inline unsigned char _Reverse_byte(unsigned char _Original_byte)
|
||
|
{
|
||
|
// return ((_Original_byte * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
|
||
|
return _Byte_reverse_table[_Original_byte];
|
||
|
}
|
||
|
|
||
|
// Finds the most significant bit in a size_t
|
||
|
inline unsigned char _Get_msb(size_t _Mask)
|
||
|
{
|
||
|
unsigned long _Index = 0;
|
||
|
|
||
|
#if (defined (_M_IX86) || defined (_M_ARM))
|
||
|
_BitScanReverse(&_Index, _Mask);
|
||
|
#else /* (defined (_M_IX86) || defined (_M_ARM)) */
|
||
|
_BitScanReverse64(&_Index, _Mask);
|
||
|
#endif /* (defined (_M_IX86) || defined (_M_ARM)) */
|
||
|
|
||
|
return (unsigned char) _Index;
|
||
|
}
|
||
|
|
||
|
#pragma warning(push)
|
||
|
#pragma warning(disable: 4127) // Warning 4127 -- while (true) has a constant expression in it
|
||
|
|
||
|
template <typename _Traits>
|
||
|
class _Concurrent_hash : public _Traits
|
||
|
{
|
||
|
public:
|
||
|
// Type definitions
|
||
|
typedef _Concurrent_hash<_Traits> _Mytype;
|
||
|
typedef typename _Split_ordered_list<typename _Traits::value_type, typename _Traits::allocator_type> _Mylist;
|
||
|
typedef typename _Traits::_Key_compare _Key_compare;
|
||
|
typedef typename _Traits::_Value_compare _Value_compare;
|
||
|
|
||
|
typedef typename _Traits::value_type value_type;
|
||
|
typedef typename _Traits::key_type key_type;
|
||
|
typedef typename _Traits::allocator_type allocator_type;
|
||
|
typedef typename allocator_type::pointer pointer;
|
||
|
typedef typename allocator_type::const_pointer const_pointer;
|
||
|
typedef typename allocator_type::reference reference;
|
||
|
typedef typename allocator_type::const_reference const_reference;
|
||
|
|
||
|
typedef typename allocator_type::size_type size_type;
|
||
|
typedef typename allocator_type::difference_type difference_type;
|
||
|
|
||
|
typedef typename std::tr1::conditional<std::tr1::is_same<key_type, value_type>::value, typename _Mylist::const_iterator, typename _Mylist::iterator>::type iterator;
|
||
|
typedef typename _Mylist::const_iterator const_iterator;
|
||
|
typedef iterator local_iterator;
|
||
|
typedef const_iterator const_local_iterator;
|
||
|
|
||
|
typedef typename _Mylist::_Nodeptr _Nodeptr;
|
||
|
|
||
|
// Iterators that walk the entire split-order list, including dummy nodes
|
||
|
typedef typename _Mylist::_Full_iterator _Full_iterator;
|
||
|
typedef typename _Mylist::_Full_const_iterator _Full_const_iterator;
|
||
|
|
||
|
static const size_type _Initial_bucket_number = 8; // Initial number of buckets
|
||
|
static const size_type _Initial_bucket_load = 4; // Initial maximum number of elements per bucket
|
||
|
static size_type const _Pointers_per_table = sizeof(size_type) * 8; // One bucket segment per bit
|
||
|
|
||
|
// Constructors/Destructors
|
||
|
_Concurrent_hash(size_type _Number_of_buckets = _Initial_bucket_number, const _Key_compare& _Parg = _Key_compare(), const allocator_type& _Allocator = allocator_type())
|
||
|
: _Traits(_Parg), _M_number_of_buckets(_Number_of_buckets), _M_split_ordered_list(_Allocator), _M_allocator(_Allocator), _M_maximum_bucket_size((float) _Initial_bucket_load)
|
||
|
{
|
||
|
_Init();
|
||
|
}
|
||
|
|
||
|
_Concurrent_hash(const _Concurrent_hash& _Right, const allocator_type& _Allocator) : _Traits(_Right._M_comparator), _M_split_ordered_list(_Allocator), _M_allocator(_Allocator)
|
||
|
{
|
||
|
_Copy(_Right);
|
||
|
}
|
||
|
|
||
|
_Concurrent_hash(const _Concurrent_hash& _Right) : _Traits(_Right._M_comparator), _M_split_ordered_list(_Right.get_allocator()), _M_allocator(_Right.get_allocator())
|
||
|
{
|
||
|
_Init();
|
||
|
_Copy(_Right);
|
||
|
}
|
||
|
|
||
|
_Concurrent_hash(_Concurrent_hash&& _Right) : _Traits(_Right._M_comparator), _M_split_ordered_list(_Right.get_allocator()), _M_allocator(_Right.get_allocator()),
|
||
|
_M_number_of_buckets(_Initial_bucket_number), _M_maximum_bucket_size((float) _Initial_bucket_load)
|
||
|
{
|
||
|
_Init();
|
||
|
swap(_Right);
|
||
|
}
|
||
|
|
||
|
_Concurrent_hash& operator=(const _Concurrent_hash& _Right)
|
||
|
{
|
||
|
if (this != &_Right)
|
||
|
{
|
||
|
_Copy(_Right);
|
||
|
}
|
||
|
|
||
|
return (*this);
|
||
|
}
|
||
|
|
||
|
_Concurrent_hash& operator=(_Concurrent_hash&& _Right)
|
||
|
{
|
||
|
if (this != &_Right)
|
||
|
{
|
||
|
clear();
|
||
|
swap(_Right);
|
||
|
}
|
||
|
|
||
|
return (*this);
|
||
|
}
|
||
|
|
||
|
~_Concurrent_hash()
|
||
|
{
|
||
|
// Delete all node segments
|
||
|
for (size_type _Index = 0; _Index < _Pointers_per_table; _Index++)
|
||
|
{
|
||
|
if (_M_buckets[_Index] != NULL)
|
||
|
{
|
||
|
size_type _Seg_size = _Segment_size(_Index);
|
||
|
for (size_type _Index2 = 0; _Index2 < _Seg_size; _Index2++)
|
||
|
{
|
||
|
_M_allocator.destroy(&_M_buckets[_Index][_Index2]);
|
||
|
}
|
||
|
_M_allocator.deallocate(_M_buckets[_Index], _Seg_size);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static size_type __cdecl _Segment_index_of( size_type _Index )
|
||
|
{
|
||
|
return size_type( _Get_msb( _Index|1 ) );
|
||
|
}
|
||
|
|
||
|
static size_type _Segment_base( size_type _K )
|
||
|
{
|
||
|
return (size_type(1)<<_K & ~size_type(1));
|
||
|
}
|
||
|
|
||
|
static size_type _Segment_size( size_type _K )
|
||
|
{
|
||
|
return _K ? size_type(1)<<_K : 2;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the stored allocator object for this concurrent container. This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// The stored allocator object for this concurrent container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
allocator_type get_allocator() const
|
||
|
{
|
||
|
return _M_split_ordered_list.get_allocator();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Tests whether no elements are present. This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <remarks>
|
||
|
/// In the presence of concurrent inserts, whether or not the concurrent container is empty may change immediately
|
||
|
/// after calling this function, before the return value is even read.
|
||
|
/// </remarks>
|
||
|
/// <returns>
|
||
|
/// <c>true</c> if the concurrent container is empty, <c>false</c> otherwise.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
bool empty() const
|
||
|
{
|
||
|
return _M_split_ordered_list.empty();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the number of elements in this concurrent container. This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <remarks>
|
||
|
/// In the presence of concurrent inserts, the number of elements in the concurrent container may change immediately
|
||
|
/// after calling this function, before the return value is even read.
|
||
|
/// </remarks>
|
||
|
/// <returns>
|
||
|
/// The number of items in the container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
size_type size() const
|
||
|
{
|
||
|
return _M_split_ordered_list.size();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the maximum size of the concurrent container, determined by the allocator. This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <remarks>
|
||
|
/// This upper bound value may actually be higher than what the container can actually hold.
|
||
|
/// </remarks>
|
||
|
/// <returns>
|
||
|
/// The maximum number of elements that can be inserted into this concurrent container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
size_type max_size() const
|
||
|
{
|
||
|
return _M_split_ordered_list.max_size();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an iterator pointing to the first element in the concurrent container. This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// An iterator to the first element in the concurrent container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
iterator begin()
|
||
|
{
|
||
|
return _M_split_ordered_list.begin();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an iterator pointing to the first element in the concurrent container. This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// An iterator to the first element in the concurrent container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
const_iterator begin() const
|
||
|
{
|
||
|
return _M_split_ordered_list.begin();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an iterator pointing to the location succeeding the last element in the concurrent container.
|
||
|
/// This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// An iterator to the location succeeding the last element in the concurrent container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
iterator end()
|
||
|
{
|
||
|
return _M_split_ordered_list.end();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns a const_iterator pointing to the location succeeding the last element in the concurrent container.
|
||
|
/// This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// A const_iterator to the location succeeding the last element in the concurrent container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
const_iterator end() const
|
||
|
{
|
||
|
return _M_split_ordered_list.end();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns a const iterator pointing to the first element in the concurrent container. This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// A const iterator to the first element in the concurrent container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
const_iterator cbegin() const
|
||
|
{
|
||
|
return _M_split_ordered_list.cbegin();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns a const iterator pointing to the location succeeding the last element in the concurrent container. This method is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// A const iterator to the location succeeding the last element in the concurrent container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
const_iterator cend() const
|
||
|
{
|
||
|
return _M_split_ordered_list.cend();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes elements from the container at specified positions. This method is not concurrency-safe.
|
||
|
/// </summary>
|
||
|
/// <param name="_Where">
|
||
|
/// The iterator position to erase from.
|
||
|
/// </param>
|
||
|
/// <remarks>
|
||
|
/// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second
|
||
|
/// member function removes the elements in the range [<paramref name="_First"/>, <paramref name="_Last"/>).
|
||
|
/// <para>The third member function removes the elements in the range delimited by equal_range(<paramref name="_Keyval"/>)</see>. </para>
|
||
|
/// </remarks>
|
||
|
/// <returns>
|
||
|
/// The first two member functions return an iterator that designates the first element remaining beyond any elements removed,
|
||
|
/// or end() if no such element exists. The third member function returns the number of elements it removes.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
iterator unsafe_erase(const_iterator _Where)
|
||
|
{
|
||
|
if (_Where == end())
|
||
|
{
|
||
|
return end();
|
||
|
}
|
||
|
|
||
|
return _Erase(_Where);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes elements from the container at specified positions. This method is not concurrency-safe.
|
||
|
/// </summary>
|
||
|
/// <param name="_First">
|
||
|
/// The position of the first element in the range of elements to be erased.
|
||
|
/// </param>
|
||
|
/// <param name="_Last">
|
||
|
/// The position of the first element beyond the range of elements to be erased.
|
||
|
/// </param>
|
||
|
/// <remarks>
|
||
|
/// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second
|
||
|
/// member function removes the elements in the range [<paramref name="_First"/>, <paramref name="_Last"/>).
|
||
|
/// <para>The third member function removes the elements in the range delimited by equal_range(<paramref name="_Keyval"/>)</see>. </para>
|
||
|
/// </remarks>
|
||
|
/// <returns>
|
||
|
/// The first two member functions return an iterator that designates the first element remaining beyond any elements removed,
|
||
|
/// or end() if no such element exists. The third member function returns the number of elements it removes.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
iterator unsafe_erase(const_iterator _First, const_iterator _Last)
|
||
|
{
|
||
|
while (_First != _Last)
|
||
|
{
|
||
|
unsafe_erase(_First++);
|
||
|
}
|
||
|
|
||
|
return _M_split_ordered_list._Get_iterator(_First);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes elements from the container at specified positions. This method is not concurrency-safe.
|
||
|
/// </summary>
|
||
|
/// <param name="_Keyval">
|
||
|
/// The key value to erase.
|
||
|
/// </param>
|
||
|
/// <remarks>
|
||
|
/// The first member function removes the element of the controlled sequence pointed to by <paramref name="_Where"/>. The second
|
||
|
/// member function removes the elements in the range [<paramref name="_First"/>, <paramref name="_Last"/>).
|
||
|
/// <para>The third member function removes the elements in the range delimited by equal_range(<paramref name="_Keyval"/>)</see>. </para>
|
||
|
/// </remarks>
|
||
|
/// <returns>
|
||
|
/// The first two member functions return an iterator that designates the first element remaining beyond any elements removed,
|
||
|
/// or end() if no such element exists. The third member function returns the number of elements it removes.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
size_type unsafe_erase(const key_type& _Keyval)
|
||
|
{
|
||
|
std::pair<iterator, iterator> _Where = equal_range(_Keyval);
|
||
|
size_type _Count = _Distance(_Where.first, _Where.second);
|
||
|
unsafe_erase(_Where.first, _Where.second);
|
||
|
return _Count;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Swaps the contents of two concurrent containers. This function is not concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <param name="_Right">
|
||
|
/// The container to swap elements from.
|
||
|
/// </param>
|
||
|
/// <remarks>
|
||
|
/// The member function throws an <see cref="invalid_argument Class">invalid_argument</see> exception if the swap is being
|
||
|
/// performed on unequal allocators.
|
||
|
/// </remarks>
|
||
|
/**/
|
||
|
void swap(_Concurrent_hash& _Right)
|
||
|
{
|
||
|
if (this != &_Right)
|
||
|
{
|
||
|
std::_Swap_adl(_M_comparator, _Right._M_comparator);
|
||
|
_M_split_ordered_list.swap(_Right._M_split_ordered_list);
|
||
|
_Swap_buckets(_Right);
|
||
|
std::swap(_M_number_of_buckets, _Right._M_number_of_buckets);
|
||
|
std::swap(_M_maximum_bucket_size, _Right._M_maximum_bucket_size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Erases all the elements in the concurrent container. This function is not concurrency safe.
|
||
|
/// </summary>
|
||
|
/**/
|
||
|
void clear()
|
||
|
{
|
||
|
// Clear list
|
||
|
_M_split_ordered_list.clear();
|
||
|
|
||
|
// Clear buckets
|
||
|
for (size_type _Index = 0; _Index < _Pointers_per_table; _Index++)
|
||
|
{
|
||
|
if (_M_buckets[_Index] != NULL)
|
||
|
{
|
||
|
size_type _Seg_size = _Segment_size(_Index);
|
||
|
for (size_type _Index2 = 0; _Index2 < _Seg_size; _Index2++)
|
||
|
{
|
||
|
_M_allocator.destroy(&_M_buckets[_Index][_Index2]);
|
||
|
}
|
||
|
_M_allocator.deallocate(_M_buckets[_Index], _Seg_size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// memset all the buckets to zero and initialize the dummy node 0
|
||
|
_Init();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Finds an element that matches a specified key. This function is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <param name="_Keyval">
|
||
|
/// The key value to search for.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// An iterator pointing to the location of the the first element that matched the key provided,
|
||
|
/// or the iterator <c>end()</c> if no such element exists.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
iterator find(const key_type& _Keyval)
|
||
|
{
|
||
|
return _Find(_Keyval);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Finds an element that matches a specified key. This function is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <param name="_Keyval">
|
||
|
/// The key value to search for.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// An iterator pointing to the location of the the first element that matched the key provided,
|
||
|
/// or the iterator <c>end()</c> if no such element exists.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
const_iterator find(const key_type& _Keyval) const
|
||
|
{
|
||
|
return _Find(_Keyval);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Counts the number of elements matching a specified key. This function is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <param name="_Keyval">
|
||
|
/// The key to search for.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// The number of times number of times the key appears in the container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
size_type count(const key_type& _Keyval) const
|
||
|
{
|
||
|
size_type _Count = 0;
|
||
|
const_iterator _It = _Find(_Keyval);
|
||
|
for (;_It != end() && !_M_comparator(_Key_function(*_It), _Keyval); _It++)
|
||
|
{
|
||
|
_Count++;
|
||
|
}
|
||
|
return _Count;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Finds a range that matches a specified key. This function is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <param name="_Keyval">
|
||
|
/// The key value to search for.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// A <see cref="pair Class">pair</see> where the first element is an iterator to the beginning and the second element
|
||
|
/// is an iterator to the end of the range.
|
||
|
/// </returns>
|
||
|
/// <remarks>
|
||
|
/// <para>It is possible for concurrent inserts to cause additional keys to be inserted after the begin iterator and
|
||
|
/// before the end iterator.</para>
|
||
|
/// </remarks>
|
||
|
/**/
|
||
|
std::pair<iterator, iterator> equal_range(const key_type& _Keyval)
|
||
|
{
|
||
|
return _Equal_range(_Keyval);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Finds a range that matches a specified key. This function is concurrency safe.
|
||
|
/// </summary>
|
||
|
/// <param name="_Keyval">
|
||
|
/// The key value to search for.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// A <see cref="pair Class">pair</see> where the first element is an iterator to the beginning and the second element
|
||
|
/// is an iterator to the end of the range.
|
||
|
/// </returns>
|
||
|
/// <remarks>
|
||
|
/// <para>It is possible for concurrent inserts to cause additional keys to be inserted after the begin iterator and
|
||
|
/// before the end iterator.</para>
|
||
|
/// </remarks>
|
||
|
/**/
|
||
|
std::pair<const_iterator, const_iterator> equal_range(const key_type& _Keyval) const
|
||
|
{
|
||
|
return _Equal_range(_Keyval);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the current number of buckets in this container.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// The current number of buckets in this container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
size_type unsafe_bucket_count() const
|
||
|
{
|
||
|
return _M_number_of_buckets;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the maximum number of buckets in this container.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// The maximum number of buckets in this container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
size_type unsafe_max_bucket_count() const
|
||
|
{
|
||
|
return _Segment_size(_Pointers_per_table-1);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the number of items in a specific bucket of this container.
|
||
|
/// </summary>
|
||
|
/// <param name="_Bucket">
|
||
|
/// The bucket to search for.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// The current number of buckets in this container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
size_type unsafe_bucket_size(size_type _Bucket)
|
||
|
{
|
||
|
size_type _Count = 0;
|
||
|
|
||
|
if (!_Is_initialized(_Bucket))
|
||
|
{
|
||
|
return _Count;
|
||
|
}
|
||
|
|
||
|
_Full_iterator _Iterator = _Get_bucket(_Bucket);
|
||
|
_Iterator++;
|
||
|
|
||
|
for (; _Iterator != _M_split_ordered_list._End() && !_Iterator._Mynode()->_Is_dummy(); _Iterator++)
|
||
|
{
|
||
|
_Count++;
|
||
|
}
|
||
|
|
||
|
return _Count;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the bucket index that a specific key maps to in this container.
|
||
|
/// </summary>
|
||
|
/// <param name="_Keyval">
|
||
|
/// The element key being searched for.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// The bucket index for the key in this container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
size_type unsafe_bucket(const key_type& _Keyval) const
|
||
|
{
|
||
|
_Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
|
||
|
size_type _Bucket = _Order_key % _M_number_of_buckets;
|
||
|
return _Bucket;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an iterator to the first element in this container for a specific bucket.
|
||
|
/// </summary>
|
||
|
/// <param name="_Bucket">
|
||
|
/// The bucket index.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// An iterator pointing to the beginning of the bucket.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
local_iterator unsafe_begin(size_type _Bucket)
|
||
|
{
|
||
|
// It is possible the bucket being searched for has not yet been initialized
|
||
|
if (!_Is_initialized(_Bucket))
|
||
|
{
|
||
|
_Initialize_bucket(_Bucket);
|
||
|
}
|
||
|
|
||
|
_Full_iterator _Iterator = _Get_bucket(_Bucket);
|
||
|
return _M_split_ordered_list._Get_first_real_iterator(_Iterator);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an iterator to the first element in this container for a specific bucket.
|
||
|
/// </summary>
|
||
|
/// <param name="_Bucket">
|
||
|
/// The bucket index.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// An iterator pointing to the beginning of the bucket.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
const_local_iterator unsafe_begin(size_type _Bucket) const
|
||
|
{
|
||
|
// It is possible the bucket being searched for has not yet been initialized
|
||
|
if (!_Is_initialized(_Bucket))
|
||
|
{
|
||
|
_Initialize_bucket(_Bucket);
|
||
|
}
|
||
|
|
||
|
|
||
|
_Full_const_iterator _Iterator = _Get_bucket(_Bucket);
|
||
|
return _M_split_ordered_list._Get_first_real_iterator(_Iterator);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an iterator to the last element in this container for a specific bucket.
|
||
|
/// </summary>
|
||
|
/// <param name="_Bucket">
|
||
|
/// The bucket index.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// An iterator pointing to the end of the bucket.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
local_iterator unsafe_end(size_type _Bucket)
|
||
|
{
|
||
|
// If we've increased the number of buckets, there's a chance the intermediate dummy
|
||
|
// node marking the end of this bucket has not yet been lazily initialized.
|
||
|
// Inserting from _M_number_of_buckets/2 to _M_number_of_buckets will recursively
|
||
|
// initialize all the dummy nodes in the map.
|
||
|
for(size_type _Bucket_index = _M_number_of_buckets >> 1; _Bucket_index < _M_number_of_buckets; _Bucket_index++)
|
||
|
{
|
||
|
if (!_Is_initialized(_Bucket_index))
|
||
|
{
|
||
|
_Initialize_bucket(_Bucket_index);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_Full_iterator _Iterator = _Get_bucket(_Bucket);
|
||
|
|
||
|
// Find the end of the bucket, denoted by the dummy element
|
||
|
do
|
||
|
{
|
||
|
_Iterator++;
|
||
|
}
|
||
|
while(_Iterator != _M_split_ordered_list._End() && !_Iterator._Mynode()->_Is_dummy());
|
||
|
|
||
|
// Return the first real element past the end of the bucket
|
||
|
return _M_split_ordered_list._Get_first_real_iterator(_Iterator);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an iterator to the last element in this container for a specific bucket.
|
||
|
/// </summary>
|
||
|
/// <param name="_Bucket">
|
||
|
/// The bucket index.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// An iterator pointing to the end of the bucket.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
const_local_iterator unsafe_end(size_type _Bucket) const
|
||
|
{
|
||
|
// If we've increased the number of buckets, there's a chance the intermediate dummy
|
||
|
// node marking the end of this bucket has not yet been lazily initialized.
|
||
|
// Inserting from _M_number_of_buckets/2 to _M_number_of_buckets will recursively
|
||
|
// initialize all the dummy nodes in the map.
|
||
|
for(size_type _Bucket_index = _M_number_of_buckets >> 1; _Bucket_index < _M_number_of_buckets; _Bucket_index++)
|
||
|
{
|
||
|
if (!_Is_initialized(_Bucket_index))
|
||
|
{
|
||
|
_Initialize_bucket(_Bucket_index);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
_Full_const_iterator _Iterator = _Get_bucket(_Bucket);
|
||
|
|
||
|
// Find the end of the bucket, denoted by the dummy element
|
||
|
do
|
||
|
{
|
||
|
_Iterator++;
|
||
|
}
|
||
|
while(_Iterator != _M_split_ordered_list._End() && !_Iterator._Mynode()->_Is_dummy());
|
||
|
|
||
|
// Return the first real element past the end of the bucket
|
||
|
return _M_split_ordered_list._Get_first_real_iterator(_Iterator);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an iterator to the first element in this container for a specific bucket.
|
||
|
/// </summary>
|
||
|
/// <param name="_Bucket">
|
||
|
/// The bucket index.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// An iterator pointing to the beginning of the bucket.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
const_local_iterator unsafe_cbegin(size_type) const
|
||
|
{
|
||
|
return ((const _Mytype *) this)->begin();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns an iterator to the first element in this container for a specific bucket.
|
||
|
/// </summary>
|
||
|
/// <param name="_Bucket">
|
||
|
/// The bucket index.
|
||
|
/// </param>
|
||
|
/// <returns>
|
||
|
/// An iterator pointing to the beginning of the bucket.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
const_local_iterator unsafe_cend(size_type) const
|
||
|
{
|
||
|
return ((const _Mytype *) this)->end();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Computes and returns the current load factor of the container. The load factor is the number of
|
||
|
/// elements in the container divided by the number of buckets.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// The load factor for the container.
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
float load_factor() const
|
||
|
{
|
||
|
return (float) size() / (float) unsafe_bucket_count();
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets or sets the maximum load factor of the container. The maximum load factor is the
|
||
|
/// largest number of elements than can be in any bucket before the container grows its
|
||
|
/// internal table.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// The first member function returns the stored maximum load factor. The second member function does not return a value
|
||
|
/// but throws an <see cref="out_of_range Class">out_of_range</see> exception if the supplied load factor is invalid..
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
float max_load_factor() const
|
||
|
{
|
||
|
return _M_maximum_bucket_size;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Gets or sets the maximum load factor of the container. The maximum load factor is the
|
||
|
/// largest number of elements than can be in any bucket before the container grows its
|
||
|
/// internal table.
|
||
|
/// </summary>
|
||
|
/// <returns>
|
||
|
/// The first member function returns the stored maximum load factor. The second member function does not return a value
|
||
|
/// but throws an <see cref="out_of_range Class">out_of_range</see> exception if the supplied load factor is invalid..
|
||
|
/// </returns>
|
||
|
/**/
|
||
|
void max_load_factor(float _Newmax)
|
||
|
{
|
||
|
// The _Newmax != _Newmax is a check for NaN, because NaN is != to itself
|
||
|
if (_Newmax != _Newmax || _Newmax < 0)
|
||
|
{
|
||
|
throw std::out_of_range("invalid hash load factor");
|
||
|
}
|
||
|
|
||
|
_M_maximum_bucket_size = _Newmax;
|
||
|
}
|
||
|
|
||
|
// This function is a no op, because the underlying split-ordered list
|
||
|
// is already sorted, so an increase in the bucket number will be
|
||
|
// reflected next time this bucket is touched.
|
||
|
|
||
|
/// <summary>
|
||
|
/// Rebuilds the hash table.
|
||
|
/// </summary>
|
||
|
/// <param name="_Buckets">
|
||
|
/// The desired number of buckets.
|
||
|
/// </param>
|
||
|
/// <remarks>
|
||
|
/// The member function alters the number of buckets to be at least <paramref name="_Buckets"/> and rebuilds the hash
|
||
|
/// table as needed. The number of buckets must be a power of 2. If not a power of 2, it will be rounded up to
|
||
|
/// the next largest power of 2.
|
||
|
/// <para>It throws an <see cref="out_of_range Class">out_of_range</see> exception if the number of buckets
|
||
|
/// is invalid (either 0 or greater than the maximum number of buckets).</para>
|
||
|
/// </remarks>
|
||
|
/**/
|
||
|
void rehash(size_type _Buckets)
|
||
|
{
|
||
|
size_type _Current_buckets = _M_number_of_buckets;
|
||
|
|
||
|
if (_Current_buckets > _Buckets)
|
||
|
{
|
||
|
return;
|
||
|
}
|
||
|
else if (_Buckets <= 0 || _Buckets > unsafe_max_bucket_count())
|
||
|
{
|
||
|
throw std::out_of_range("invalid number of buckets");
|
||
|
}
|
||
|
// Round up the number of buckets to the next largest power of 2
|
||
|
_M_number_of_buckets = ((size_type) 1) << _Get_msb(_Buckets*2-1);
|
||
|
}
|
||
|
|
||
|
protected:
|
||
|
// Insert an element in the hash given its value
|
||
|
template<typename _ValTy>
|
||
|
std::pair<iterator, bool> _Insert(_ValTy&& _Value)
|
||
|
{
|
||
|
_Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Key_function(_Value));
|
||
|
size_type _Bucket = _Order_key % _M_number_of_buckets;
|
||
|
|
||
|
// If bucket is empty, initialize it first
|
||
|
if (!_Is_initialized(_Bucket))
|
||
|
{
|
||
|
_Initialize_bucket(_Bucket);
|
||
|
}
|
||
|
|
||
|
long _New_count;
|
||
|
_Order_key = _Split_order_regular_key(_Order_key);
|
||
|
_Full_iterator _Iterator = _Get_bucket(_Bucket);
|
||
|
_Full_iterator _Last = _M_split_ordered_list._End();
|
||
|
_Full_iterator _Where = _Iterator;
|
||
|
_Nodeptr _New_node = _M_split_ordered_list._Buynode(_Order_key, std::forward<_ValTy>(_Value));
|
||
|
|
||
|
_ASSERT_EXPR(_Where != _Last, L"Invalid head node");
|
||
|
|
||
|
// First node is a dummy node
|
||
|
_Where++;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
if (_Where == _Last || _Mylist::_Get_key(_Where) > _Order_key)
|
||
|
{
|
||
|
// Try to insert it in the right place
|
||
|
std::pair<iterator, bool> _Result = _M_split_ordered_list._Insert(_Iterator, _Where, _New_node, &_New_count);
|
||
|
|
||
|
if (_Result.second)
|
||
|
{
|
||
|
// Insertion succeeded, adjust the table size, if needed
|
||
|
_Adjust_table_size(_New_count, _M_number_of_buckets);
|
||
|
return _Result;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Insertion failed: either the same node was inserted by another thread, or
|
||
|
// another element was inserted at exactly the same place as this node.
|
||
|
// Proceed with the search from the previous location where order key was
|
||
|
// known to be larger (note: this is legal only because there is no safe
|
||
|
// concurrent erase operation supported).
|
||
|
_Where = _Iterator;
|
||
|
_Where++;
|
||
|
continue;
|
||
|
}
|
||
|
}
|
||
|
else if (!_M_allow_multimapping && _Mylist::_Get_key(_Where) == _Order_key &&
|
||
|
_M_comparator(_Key_function(*_Where), _Key_function(_New_node->_M_element)) == 0)
|
||
|
{
|
||
|
// If the insert failed (element already there), then delete the new one
|
||
|
_M_split_ordered_list._Erase(_New_node);
|
||
|
|
||
|
// Element already in the list, return it
|
||
|
return std::pair<iterator, bool>(_M_split_ordered_list._Get_iterator(_Where), false);
|
||
|
}
|
||
|
|
||
|
// Move the iterator forward
|
||
|
_Iterator = _Where;
|
||
|
_Where++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
template<class _Iterator>
|
||
|
void _Insert(_Iterator _First, _Iterator _Last)
|
||
|
{
|
||
|
for (_Iterator _I = _First; _I != _Last; _I++)
|
||
|
{
|
||
|
_Insert(*_I);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
|
||
|
// Initialize the hash and keep the first bucket open
|
||
|
void _Init()
|
||
|
{
|
||
|
// Allocate an array of segment pointers
|
||
|
memset(_M_buckets, 0, _Pointers_per_table * sizeof(void *));
|
||
|
|
||
|
// Insert the first element in the split-ordered list
|
||
|
_Full_iterator _Dummy_node = _M_split_ordered_list._Begin();
|
||
|
_Set_bucket(0, _Dummy_node);
|
||
|
}
|
||
|
|
||
|
void _Copy(const _Mytype& _Right)
|
||
|
{
|
||
|
clear();
|
||
|
|
||
|
_M_maximum_bucket_size = _Right._M_maximum_bucket_size;
|
||
|
_M_number_of_buckets = _Right._M_number_of_buckets;
|
||
|
|
||
|
try
|
||
|
{
|
||
|
_Insert(_Right.begin(), _Right.end());
|
||
|
_M_comparator = _Right._M_comparator;
|
||
|
}
|
||
|
catch(...)
|
||
|
{
|
||
|
_M_split_ordered_list.clear();
|
||
|
throw;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void _Swap_buckets(_Concurrent_hash& _Right)
|
||
|
{
|
||
|
if (_M_allocator == _Right._M_allocator)
|
||
|
{
|
||
|
// Swap all node segments
|
||
|
for (size_type _Index = 0; _Index < _Pointers_per_table; _Index++)
|
||
|
{
|
||
|
_Full_iterator * _Iterator_pointer = _M_buckets[_Index];
|
||
|
_M_buckets[_Index] = _Right._M_buckets[_Index];
|
||
|
_Right._M_buckets[_Index] = _Iterator_pointer;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
throw std::invalid_argument("swap is invalid on non-equal allocators");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Hash APIs
|
||
|
size_type _Distance(const_iterator _First, const_iterator _Last) const
|
||
|
{
|
||
|
size_type _Num = 0;
|
||
|
|
||
|
for (const_iterator _Iterator = _First; _Iterator != _Last; _Iterator++)
|
||
|
{
|
||
|
_Num++;
|
||
|
}
|
||
|
|
||
|
return _Num;
|
||
|
}
|
||
|
|
||
|
// Find the element in the split-ordered list
|
||
|
iterator _Find(const key_type& _Keyval)
|
||
|
{
|
||
|
_Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
|
||
|
size_type _Bucket = _Order_key % _M_number_of_buckets;
|
||
|
|
||
|
// If _Bucket is empty, initialize it first
|
||
|
if (!_Is_initialized(_Bucket))
|
||
|
{
|
||
|
_Initialize_bucket(_Bucket);
|
||
|
}
|
||
|
|
||
|
_Order_key = _Split_order_regular_key(_Order_key);
|
||
|
_Full_iterator _Last = _M_split_ordered_list._End();
|
||
|
|
||
|
for (_Full_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++)
|
||
|
{
|
||
|
if (_Mylist::_Get_key(_Iterator) > _Order_key)
|
||
|
{
|
||
|
// If the order key is smaller than the current order key, the element
|
||
|
// is not in the hash.
|
||
|
return end();
|
||
|
}
|
||
|
else if (_Mylist::_Get_key(_Iterator) == _Order_key)
|
||
|
{
|
||
|
// The fact that order keys match does not mean that the element is found.
|
||
|
// Key function comparison has to be performed to check whether this is the
|
||
|
// right element. If not, keep searching while order key is the same.
|
||
|
if (!_M_comparator(_Key_function(*_Iterator), _Keyval))
|
||
|
{
|
||
|
return _M_split_ordered_list._Get_iterator(_Iterator);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return end();
|
||
|
}
|
||
|
|
||
|
// Find the element in the split-ordered list
|
||
|
const_iterator _Find(const key_type& _Keyval) const
|
||
|
{
|
||
|
_Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
|
||
|
size_type _Bucket = _Order_key % _M_number_of_buckets;
|
||
|
|
||
|
// If _Bucket has not been initialized, keep searching up for a parent bucket
|
||
|
// that has been initialized. Worst case is the entire map will be read.
|
||
|
while (!_Is_initialized(_Bucket))
|
||
|
{
|
||
|
_Bucket = _Get_parent(_Bucket);
|
||
|
}
|
||
|
|
||
|
_Order_key = _Split_order_regular_key(_Order_key);
|
||
|
_Full_const_iterator _Last = _M_split_ordered_list._End();
|
||
|
|
||
|
for (_Full_const_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++)
|
||
|
{
|
||
|
if (_Mylist::_Get_key(_Iterator) > _Order_key)
|
||
|
{
|
||
|
// If the order key is smaller than the current order key, the element
|
||
|
// is not in the hash.
|
||
|
return end();
|
||
|
}
|
||
|
else if (_Mylist::_Get_key(_Iterator) == _Order_key)
|
||
|
{
|
||
|
// The fact that order keys match does not mean that the element is found.
|
||
|
// Key function comparison has to be performed to check whether this is the
|
||
|
// right element. If not, keep searching while order key is the same.
|
||
|
if (!_M_comparator(_Key_function(*_Iterator), _Keyval))
|
||
|
{
|
||
|
return _M_split_ordered_list._Get_iterator(_Iterator);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return end();
|
||
|
}
|
||
|
|
||
|
// Erase an element from the list. This is not a concurrency safe function.
|
||
|
iterator _Erase(const_iterator _Iterator)
|
||
|
{
|
||
|
_Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Key_function(*_Iterator));
|
||
|
size_type _Bucket = _Order_key % _M_number_of_buckets;
|
||
|
|
||
|
// If bucket is empty, initialize it first
|
||
|
if (!_Is_initialized(_Bucket))
|
||
|
{
|
||
|
_Initialize_bucket(_Bucket);
|
||
|
}
|
||
|
|
||
|
_Order_key = _Split_order_regular_key(_Order_key);
|
||
|
|
||
|
_Full_iterator _Previous = _Get_bucket(_Bucket);
|
||
|
_Full_iterator _Last = _M_split_ordered_list._End();
|
||
|
_Full_iterator _Where = _Previous;
|
||
|
|
||
|
_ASSERT_EXPR(_Where != _Last, L"Invalid head node");
|
||
|
|
||
|
// First node is a dummy node
|
||
|
_Where++;
|
||
|
|
||
|
for (;;)
|
||
|
{
|
||
|
if (_Where == _Last)
|
||
|
{
|
||
|
return end();
|
||
|
}
|
||
|
else if (_M_split_ordered_list._Get_iterator(_Where) == _Iterator)
|
||
|
{
|
||
|
return _M_split_ordered_list._Erase(_Previous, _Iterator);
|
||
|
}
|
||
|
|
||
|
// Move the iterator forward
|
||
|
_Previous = _Where;
|
||
|
_Where++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Return the [begin, end] pair of iterators with the same key values.
|
||
|
// This operation makes sense only if mapping is many-to-one.
|
||
|
std::pair<iterator, iterator> _Equal_range(const key_type& _Keyval)
|
||
|
{
|
||
|
_Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
|
||
|
size_type _Bucket = _Order_key % _M_number_of_buckets;
|
||
|
|
||
|
// If _Bucket is empty, initialize it first
|
||
|
if (!_Is_initialized(_Bucket))
|
||
|
{
|
||
|
_Initialize_bucket(_Bucket);
|
||
|
}
|
||
|
|
||
|
_Order_key = _Split_order_regular_key(_Order_key);
|
||
|
_Full_iterator _Last = _M_split_ordered_list._End();
|
||
|
|
||
|
for (_Full_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++)
|
||
|
{
|
||
|
if (_Mylist::_Get_key(_Iterator) > _Order_key)
|
||
|
{
|
||
|
// There is no element with the given key
|
||
|
return std::pair<iterator, iterator>(end(), end());
|
||
|
}
|
||
|
else if (_Mylist::_Get_key(_Iterator) == _Order_key && !_M_comparator(_Key_function(*_Iterator), _Keyval))
|
||
|
{
|
||
|
iterator _Begin = _M_split_ordered_list._Get_iterator(_Iterator);
|
||
|
iterator _End= _Begin;
|
||
|
|
||
|
for (;_End != end() && !_M_comparator(_Key_function(*_End), _Keyval); _End++)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
return std::pair<iterator, iterator>(_Begin, _End);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return std::pair<iterator, iterator>(end(), end());
|
||
|
}
|
||
|
|
||
|
// Return the [begin, end] pair of const iterators with the same key values.
|
||
|
// This operation makes sense only if mapping is many-to-one.
|
||
|
std::pair<const_iterator, const_iterator> _Equal_range(const key_type& _Keyval) const
|
||
|
{
|
||
|
_Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
|
||
|
size_type _Bucket = _Order_key % _M_number_of_buckets;
|
||
|
|
||
|
// If _Bucket has not been initialized, keep searching up for a parent bucket
|
||
|
// that has been initialized. Worst case is the entire map will be read.
|
||
|
while (!_Is_initialized(_Bucket))
|
||
|
{
|
||
|
_Bucket = _Get_parent(_Bucket);
|
||
|
}
|
||
|
|
||
|
_Order_key = _Split_order_regular_key(_Order_key);
|
||
|
_Full_const_iterator _Last = _M_split_ordered_list._End();
|
||
|
|
||
|
for (_Full_const_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++)
|
||
|
{
|
||
|
if (_Mylist::_Get_key(_Iterator) > _Order_key)
|
||
|
{
|
||
|
// There is no element with the given key
|
||
|
return std::pair<const_iterator, const_iterator>(end(), end());
|
||
|
}
|
||
|
else if (_Mylist::_Get_key(_Iterator) == _Order_key && !_M_comparator(_Key_function(*_Iterator), _Keyval))
|
||
|
{
|
||
|
const_iterator _Begin = _M_split_ordered_list._Get_iterator(_Iterator);
|
||
|
const_iterator _End = _Begin;
|
||
|
|
||
|
for (; _End != end() && !_M_comparator(_Key_function(*_End), _Keyval); _End++)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
return std::pair<const_iterator, const_iterator>(_Begin, _End);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return std::pair<const_iterator, const_iterator>(end(), end());
|
||
|
}
|
||
|
|
||
|
// Bucket APIs
|
||
|
void _Initialize_bucket(size_type _Bucket)
|
||
|
{
|
||
|
// Bucket 0 has no parent. Initialize it and return.
|
||
|
if (_Bucket == 0)
|
||
|
{
|
||
|
_Init();
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
size_type _Parent_bucket = _Get_parent(_Bucket);
|
||
|
|
||
|
// All _Parent_bucket buckets have to be initialized before this bucket is
|
||
|
if (!_Is_initialized(_Parent_bucket))
|
||
|
{
|
||
|
_Initialize_bucket(_Parent_bucket);
|
||
|
}
|
||
|
|
||
|
_Full_iterator _Parent = _Get_bucket(_Parent_bucket);
|
||
|
|
||
|
// Create a dummy first node in this bucket
|
||
|
_Full_iterator _Dummy_node = _M_split_ordered_list._Insert_dummy(_Parent, _Split_order_dummy_key(_Bucket));
|
||
|
_Set_bucket(_Bucket, _Dummy_node);
|
||
|
}
|
||
|
|
||
|
void _Adjust_table_size(size_type _Total_elements, size_type _Current_size)
|
||
|
{
|
||
|
// Grow the table by a factor of 2 if possible and needed
|
||
|
if (((float) _Total_elements / (float) _Current_size) > _M_maximum_bucket_size)
|
||
|
{
|
||
|
// Double the size of the hash only if size has not changed inbetween loads
|
||
|
_InterlockedCompareExchangeSizeT(&_M_number_of_buckets, 2 * _Current_size, _Current_size);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
size_type _Get_parent(size_type _Bucket) const
|
||
|
{
|
||
|
// Unsets bucket's most significant turned-on bit
|
||
|
unsigned char _Msb = _Get_msb(_Bucket);
|
||
|
return _Bucket & ~(1 << _Msb);
|
||
|
}
|
||
|
|
||
|
// Dynamic sized array (segments)
|
||
|
_Full_iterator _Get_bucket(size_type _Bucket) const
|
||
|
{
|
||
|
size_type _Segment = _Segment_index_of(_Bucket);
|
||
|
_Bucket -= _Segment_base(_Segment);
|
||
|
return _M_buckets[_Segment][_Bucket];
|
||
|
}
|
||
|
|
||
|
void _Set_bucket(size_type _Bucket, _Full_iterator _Dummy_head)
|
||
|
{
|
||
|
size_type _Segment = _Segment_index_of(_Bucket);
|
||
|
_Bucket -= _Segment_base(_Segment);
|
||
|
|
||
|
if (_M_buckets[_Segment] == NULL)
|
||
|
{
|
||
|
size_type _Seg_size = _Segment_size(_Segment);
|
||
|
_Full_iterator * _New_segment = _M_allocator.allocate(_Seg_size);
|
||
|
std::_Wrap_alloc<decltype(_M_allocator)> _Wrapped_allocator(_M_allocator);
|
||
|
std::_Uninitialized_default_fill_n(_New_segment, _Seg_size, _Wrapped_allocator);
|
||
|
if (_InterlockedCompareExchangePointer((void * volatile *) &_M_buckets[_Segment], _New_segment, NULL) != NULL)
|
||
|
{
|
||
|
_M_allocator.deallocate(_New_segment, _Seg_size);
|
||
|
}
|
||
|
}
|
||
|
_M_buckets[_Segment][_Bucket] = _Dummy_head;
|
||
|
}
|
||
|
|
||
|
bool _Is_initialized(size_type _Bucket) const
|
||
|
{
|
||
|
size_type _Segment = _Segment_index_of(_Bucket);
|
||
|
_Bucket -= _Segment_base(_Segment);
|
||
|
|
||
|
if (_M_buckets[_Segment] == NULL)
|
||
|
{
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
_Full_iterator _Iterator = _M_buckets[_Segment][_Bucket];
|
||
|
return (_Iterator._Mynode() != NULL);
|
||
|
}
|
||
|
|
||
|
// Utilities for keys
|
||
|
_Split_order_key _Reverse(_Map_key _Order_key) const
|
||
|
{
|
||
|
_Split_order_key _Reversed_order_key;
|
||
|
|
||
|
unsigned char * _Original = (unsigned char *) &_Order_key;
|
||
|
unsigned char * _Reversed = (unsigned char *) &_Reversed_order_key;
|
||
|
|
||
|
int _Size = sizeof(_Map_key);
|
||
|
for (int _Index = 0; _Index < _Size; _Index++)
|
||
|
{
|
||
|
_Reversed[_Size - _Index - 1] = _Reverse_byte(_Original[_Index]);
|
||
|
}
|
||
|
|
||
|
return _Reversed_order_key;
|
||
|
}
|
||
|
|
||
|
// A regular order key has its original hash value reversed and the last bit set
|
||
|
_Split_order_key _Split_order_regular_key(_Map_key _Order_key) const
|
||
|
{
|
||
|
return _Reverse(_Order_key) | 0x1;
|
||
|
}
|
||
|
|
||
|
// A dummy order key has its original hash value reversed and the last bit unset
|
||
|
_Split_order_key _Split_order_dummy_key(_Map_key _Order_key) const
|
||
|
{
|
||
|
return _Reverse(_Order_key) & ~(0x1);
|
||
|
}
|
||
|
|
||
|
// Shared variables
|
||
|
_Full_iterator * _M_buckets[_Pointers_per_table]; // The segment table
|
||
|
_Mylist _M_split_ordered_list; // List where all the elements are kept
|
||
|
typename allocator_type::template rebind<_Full_iterator>::other _M_allocator; // Allocator object for segments
|
||
|
size_type _M_number_of_buckets; // Current table size
|
||
|
float _M_maximum_bucket_size; // Maximum size of the bucket
|
||
|
};
|
||
|
|
||
|
#pragma warning(pop) // Warning 4127 -- while (true) has a constant expression in it
|
||
|
|
||
|
} // namespace details;
|
||
|
} // namespace Concurrency
|
||
|
|
||
|
namespace concurrency = Concurrency;
|
||
|
|
||
|
#pragma pack(pop)
|