4 * Copyright (c) Microsoft Corporation. All rights reserved.
\r
7 * =+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
\r
9 * internal_concurrent_hash.h
\r
11 * =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
\r
16 #include "internal_split_ordered_list.h"
\r
19 namespace Concurrency
\r
23 // Template class for hash compare
\r
24 template<typename _Key_type, typename _Hasher, typename _Key_equality>
\r
28 typedef _Hasher hasher;
\r
34 _Hash_compare(hasher _Hasharg) : _M_hash_object(_Hasharg)
\r
38 _Hash_compare(hasher _Hasharg, _Key_equality _Keyeqarg) : _M_hash_object(_Hasharg), _M_key_compare_object(_Keyeqarg)
\r
42 size_t operator()(const _Key_type& _Keyval) const
\r
44 return ((size_t)_M_hash_object(_Keyval));
\r
47 bool operator()(const _Key_type& _Keyval1, const _Key_type& _Keyval2) const
\r
49 return (!_M_key_compare_object(_Keyval1, _Keyval2));
\r
52 hasher _M_hash_object; // The hash object
\r
53 _Key_equality _M_key_compare_object; // The equality comparator object
\r
56 // An efficient implementation of the _Reverse function utilizes a 2^8 or 2^16 lookup table holding the
\r
57 // bit-reversed values of [0..2^8 - 1] or [0..2^16 - 1] respectively. Those values can also be computed
\r
58 // on the fly at a slightly higher cost.
\r
59 const unsigned char _Byte_reverse_table[] =
\r
61 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0,
\r
62 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8,
\r
63 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4,
\r
64 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC,
\r
65 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2,
\r
66 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA,
\r
67 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6,
\r
68 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE,
\r
69 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1,
\r
70 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9,
\r
71 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5,
\r
72 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD,
\r
73 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3,
\r
74 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB,
\r
75 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7,
\r
76 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF
\r
79 // Given a byte, reverses it
\r
80 inline unsigned char _Reverse_byte(unsigned char _Original_byte)
\r
82 // return ((_Original_byte * 0x80200802ULL) & 0x0884422110ULL) * 0x0101010101ULL >> 32;
\r
83 return _Byte_reverse_table[_Original_byte];
\r
86 // Finds the most significant bit in a size_t
\r
87 inline unsigned char _Get_msb(size_t _Mask)
\r
89 unsigned long _Index = 0;
\r
91 #if defined(_M_IX86)
\r
92 _BitScanReverse(&_Index, _Mask);
\r
94 _BitScanReverse64(&_Index, _Mask);
\r
97 return (unsigned char) _Index;
\r
100 #pragma warning(push)
\r
101 #pragma warning(disable: 4127) // warning 4127 -- while (true) has a constant expression in it
\r
103 template <typename _Traits>
\r
104 class _Concurrent_hash : public _Traits
\r
107 // Type definitions
\r
108 typedef _Concurrent_hash<_Traits> _Mytype;
\r
109 typedef typename _Traits::_Value_type _Value_type;
\r
110 typedef typename _Traits::value_type value_type;
\r
111 typedef typename _Traits::key_type key_type;
\r
112 typedef typename _Traits::key_compare key_compare;
\r
113 typedef typename _Traits::value_compare value_compare;
\r
115 typedef typename _Traits::allocator_type allocator_type;
\r
116 typedef typename allocator_type::pointer pointer;
\r
117 typedef typename allocator_type::const_pointer const_pointer;
\r
118 typedef typename allocator_type::reference reference;
\r
119 typedef typename allocator_type::const_reference const_reference;
\r
121 typedef typename allocator_type::size_type size_type;
\r
122 typedef typename allocator_type::difference_type difference_type;
\r
124 typedef typename _Split_ordered_list<typename _Traits::value_type, typename _Traits::allocator_type> _Mylist;
\r
125 typedef typename _Mylist::_Nodeptr _Nodeptr;
\r
127 typedef typename std::tr1::conditional<std::tr1::is_same<key_type, value_type>::value, typename _Mylist::const_iterator, typename _Mylist::iterator>::type iterator;
\r
128 typedef typename _Mylist::const_iterator const_iterator;
\r
129 typedef iterator local_iterator;
\r
130 typedef const_iterator const_local_iterator;
\r
131 typedef std::pair<iterator, bool> _Pairib;
\r
132 typedef std::pair<iterator, iterator> _Pairii;
\r
133 typedef std::pair<const_iterator, const_iterator> _Paircc;
\r
135 // Iterators that walk the entire split-order list, including dummy nodes
\r
136 typedef typename _Mylist::_Full_iterator _Full_iterator;
\r
137 typedef typename _Mylist::_Full_const_iterator _Full_const_iterator;
\r
139 static const size_type _Initial_bucket_number = 8; // Initial number of buckets
\r
140 static const size_type _Initial_bucket_load = 4; // Initial maximum number of elements per bucket
\r
141 static size_type const _Pointers_per_table = sizeof(size_type) * 8; // One bucket segment per bit
\r
143 // Constructors/Destructors
\r
144 _Concurrent_hash(size_type _Number_of_buckets = _Initial_bucket_number, const key_compare& _Parg = key_compare(), const allocator_type& _Allocator = allocator_type())
\r
145 : _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)
\r
150 _Concurrent_hash(const _Concurrent_hash& _Right, const allocator_type& _Allocator) : _Traits(_Right._M_comparator), _M_split_ordered_list(_Allocator), _M_allocator(_Allocator)
\r
155 _Concurrent_hash(const _Concurrent_hash& _Right) : _Traits(_Right._M_comparator), _M_split_ordered_list(_Right.get_allocator()), _M_allocator(_Right.get_allocator())
\r
161 _Concurrent_hash(_Concurrent_hash&& _Right) : _Traits(_Right._M_comparator), _M_split_ordered_list(_Right.get_allocator()), _M_allocator(_Right.get_allocator()),
\r
162 _M_number_of_buckets(_Initial_bucket_number), _M_maximum_bucket_size((float) _Initial_bucket_load)
\r
168 _Concurrent_hash& operator=(const _Concurrent_hash& _Right)
\r
170 if (this != &_Right)
\r
178 _Concurrent_hash& operator=(_Concurrent_hash&& _Right)
\r
180 if (this != &_Right)
\r
189 ~_Concurrent_hash()
\r
191 // Delete all node segments
\r
192 for (size_type _Index = 0; _Index < _Pointers_per_table; _Index++)
\r
194 if (_M_buckets[_Index] != NULL)
\r
196 size_type _Seg_size = _Segment_size(_Index);
\r
197 for (size_type _Index2 = 0; _Index2 < _Seg_size; _Index2++)
\r
199 _M_allocator.destroy(&_M_buckets[_Index][_Index2]);
\r
201 _M_allocator.deallocate(_M_buckets[_Index], _Seg_size);
\r
206 static size_type __cdecl _Segment_index_of( size_type _Index )
\r
208 return size_type( _Get_msb( _Index|1 ) );
\r
211 static size_type _Segment_base( size_type _K )
\r
213 return (size_type(1)<<_K & ~size_type(1));
\r
216 static size_type _Segment_size( size_type _K )
\r
218 return _K ? size_type(1)<<_K : 2;
\r
222 /// Returns the allocator used for this concurrent container.
\r
225 /// This function is concurrency safe.
\r
228 /// The allocator used for this concurrent container.
\r
231 allocator_type get_allocator() const
\r
233 return _M_split_ordered_list.get_allocator();
\r
236 // Size and capacity function
\r
238 /// Checks whether the number of elements in this concurrent container is non-zero.
\r
241 /// This function is concurrency safe.
\r
242 /// <para>With concurrent inserts, whether or not the concurrent container is empty may change
\r
243 /// immediately after calling this function, before the return value is even read.</para>
\r
246 /// True, if this concurrent container object is empty, false, otherwise.
\r
251 return _M_split_ordered_list.empty();
\r
255 /// Returns the number of elements in this concurrent container.
\r
258 /// This function is concurrency safe.
\r
259 /// <para>With concurrent inserts, the number of elements in the concurrent container may change
\r
260 /// immediately after calling this function, before the return value is even read.</para>
\r
263 /// The number of items in the container.
\r
266 size_type size() const
\r
268 return _M_split_ordered_list.size();
\r
272 /// Returns the maximum size of the concurrent container, determined by the allocator.
\r
275 /// This function is concurrency safe.
\r
276 /// <para>This upper bound value may actually be higher than what the container can actually hold.</para>
\r
279 /// The maximum number of elements that can be put into this concurrent container.
\r
282 size_type max_size() const
\r
284 return _M_split_ordered_list.max_size();
\r
289 /// Returns an iterator pointing to the first element in the concurrent container.
\r
292 /// This function is concurrency safe.
\r
295 /// An iterator to the first element in the concurrent container.
\r
300 return _M_split_ordered_list.begin();
\r
304 /// Returns a const_iterator pointing to the first element in the concurrent container.
\r
307 /// This function is concurrency safe.
\r
310 /// A const_iterator to the first element in the concurrent container.
\r
313 const_iterator begin() const
\r
315 return _M_split_ordered_list.begin();
\r
319 /// Returns an iterator pointing to the location succeeding the last element in the concurrent container.
\r
322 /// This function is concurrency safe.
\r
325 /// An iterator to the location succeeding the last element in the concurrent container.
\r
330 return _M_split_ordered_list.end();
\r
334 /// Returns a const_iterator pointing to the location succeeding the last element in the concurrent container.
\r
337 /// This function is concurrency safe.
\r
340 /// A const_iterator to the location succeeding the last element in the concurrent container.
\r
343 const_iterator end() const
\r
345 return _M_split_ordered_list.end();
\r
349 /// Returns a const_iterator pointing to the first element in the concurrent container.
\r
352 /// This function is concurrency safe.
\r
355 /// A const_iterator to the first element in the concurrent container.
\r
358 const_iterator cbegin() const
\r
360 return _M_split_ordered_list.cbegin();
\r
364 /// Returns a const_iterator pointing to the location succeeding the last element in the concurrent container.
\r
367 /// This function is concurrency safe.
\r
370 /// A const_iterator to the location succeeding the last element in the concurrent container.
\r
373 const_iterator cend() const
\r
375 return _M_split_ordered_list.cend();
\r
380 /// Inserts a value into the concurrent container.
\r
382 /// <param name="_Value">
\r
383 /// The value to insert.
\r
386 /// This function is concurrency safe.
\r
389 /// A <see cref="pair Class">pair</see> where the first object is an iterator into the container at the insertion point and the
\r
390 /// second object is a bool indicating whether the value was inserted (true) or not (false).
\r
393 _Pairib insert(const value_type& _Value)
\r
395 return _Insert(_Value);
\r
399 /// Inserts a value into the concurrent container.
\r
401 /// <typeparam name="_Valty">
\r
402 /// The type of the value inserted into the map.
\r
404 /// <param name="_Value">
\r
405 /// The value to insert.
\r
408 /// This function is concurrency safe.
\r
411 /// A <see cref="pair Class">pair</see> where the first object is an iterator into the container at the insertion point and the
\r
412 /// second object is a bool indicating whether the value was inserted (true) or not (false).
\r
415 template<class _Valty>
\r
416 _Pairib insert(_Valty&& _Value)
\r
418 return _Insert(std::forward<_Valty>(_Value));
\r
422 /// Inserts a value into the concurrent container.
\r
424 /// <param name="_Value">
\r
425 /// The value to insert.
\r
428 /// This function is concurrency safe.
\r
429 /// <para>The current implementation ignores the first <c>const_iterator</c> argument. It exists for
\r
430 /// similarity with the <see cref="unordered_map Class">unordered_map</see>, and hints to a location to start the search
\r
431 /// for an insertion point.</para>
\r
434 /// An iterator pointing to the insertion point of the object. If the key already exists in the container
\r
435 /// and the container does not support multiple keys, an iterator pointing to the duplicate key location in
\r
436 /// the map is returned.
\r
439 iterator insert(const_iterator, const value_type& _Value)
\r
442 return insert(_Value).first;
\r
446 /// Inserts a value into the concurrent container.
\r
448 /// <typeparam name="_Valty">
\r
449 /// The type of the value inserted into the map.
\r
451 /// <param name="_Value">
\r
452 /// The value to insert.
\r
455 /// This function is concurrency safe.
\r
456 /// <para>The current implementation ignores the first <c>const_iterator</c> argument. It exists for
\r
457 /// similarity with the <see cref="unordered_map Class">unordered_map</see>, and hints to a location to start the search
\r
458 /// for an insertion point.</para>
\r
461 /// An iterator pointing to the insertion point of the object. If the key already exists in the container
\r
462 /// and the container does not support multiple keys, an iterator pointing to the duplicate key location in
\r
463 /// the map is returned.
\r
466 template<class _Valty>
\r
467 typename std::tr1::enable_if<!std::tr1::is_same<const_iterator,
\r
468 typename std::tr1::remove_reference<_Valty>::type>::value, iterator>::type
\r
469 insert(const_iterator, _Valty&& _Value)
\r
472 return insert(std::forward<_Valty>(_Value)).first;
\r
476 /// Inserts a set of values into the concurrent container from an iterator.
\r
478 /// <typeparam name="_Iterator">
\r
479 /// The iterator type used for insertion.
\r
481 /// <param name="_First">
\r
482 /// The input iterator pointing to the beginning location.
\r
484 /// <param name="_Last">
\r
485 /// The input iterator pointing to the end location.
\r
488 /// This function is concurrency safe.
\r
491 template<class _Iterator>
\r
492 void insert(_Iterator _First, _Iterator _Last)
\r
494 for (_Iterator _I = _First; _I != _Last; _I++)
\r
501 /// Erases an element from the concurrent container.
\r
503 /// <param name="_Where">
\r
504 /// A <c>const_iterator</c> pointing to the element to erase.
\r
507 /// This function is not concurrency safe.
\r
510 /// An iterator pointing to the location immediately following the deleted object in the container.
\r
513 iterator unsafe_erase(const_iterator _Where)
\r
515 return _Erase(_Where);
\r
519 /// Erases multiple elements from the concurrent container.
\r
521 /// <param name="_First">
\r
522 /// A <c>const_iterator</c> pointing to the first element to erase.
\r
524 /// <param name="_Last">
\r
525 /// A <c>const_iterator</c> pointing to the location after the last element to erase,.
\r
528 /// This function is not concurrency safe.
\r
531 /// An iterator pointing to the location immediately following the deleted object(s) in the container.
\r
534 iterator unsafe_erase(const_iterator _First, const_iterator _Last)
\r
536 while (_First != _Last)
\r
538 unsafe_erase(_First++);
\r
541 return _M_split_ordered_list._Get_iterator(_First);
\r
545 /// Erases an element from the concurrent container.
\r
547 /// <param name="_Keyval">
\r
548 /// The value to erase from the concurrent container.
\r
551 /// This function is not concurrency safe.
\r
554 /// A count of the number of keys removed from the concurrent_unordered_map.
\r
557 size_type unsafe_erase(const key_type& _Keyval)
\r
559 _Pairii _Where = equal_range(_Keyval);
\r
560 size_type _Count = _Distance(_Where.first, _Where.second);
\r
561 unsafe_erase(_Where.first, _Where.second);
\r
566 /// Swaps the contents of two concurrent containers.
\r
568 /// <param name="_Right">
\r
569 /// The container to swap elements from.
\r
572 /// This function is not concurrency safe.
\r
575 /// Throws an <see cref="invalid_argument Class">invalid_argument</see> exception if the swap is being
\r
576 /// performed on unequal allocators.
\r
579 void swap(_Concurrent_hash& _Right)
\r
581 if (this != &_Right)
\r
583 std::_Swap_adl(_M_comparator, _Right._M_comparator);
\r
584 _M_split_ordered_list.swap(_Right._M_split_ordered_list);
\r
585 _Swap_buckets(_Right);
\r
586 std::swap(_M_number_of_buckets, _Right._M_number_of_buckets);
\r
587 std::swap(_M_maximum_bucket_size, _Right._M_maximum_bucket_size);
\r
593 /// Clears all the objects in the concurrent container.
\r
596 /// This function is not concurrency safe.
\r
602 _M_split_ordered_list.clear();
\r
605 for (size_type _Index = 0; _Index < _Pointers_per_table; _Index++)
\r
607 if (_M_buckets[_Index] != NULL)
\r
609 size_type _Seg_size = _Segment_size(_Index);
\r
610 for (size_type _Index2 = 0; _Index2 < _Seg_size; _Index2++)
\r
612 _M_allocator.destroy(&_M_buckets[_Index][_Index2]);
\r
614 _M_allocator.deallocate(_M_buckets[_Index], _Seg_size);
\r
618 // memset all the buckets to zero and initialize the dummy node 0
\r
624 /// Searches the concurrent container for a specific key.
\r
626 /// <param name="_Keyval">
\r
627 /// The key to search for.
\r
630 /// This function is concurrency safe.
\r
633 /// An iterator pointing to the location of the searched for key, or points to end() if not found.
\r
636 iterator find(const key_type& _Keyval)
\r
638 return _Find(_Keyval);
\r
642 /// Searches the concurrent container for a specific key.
\r
644 /// <param name="_Keyval">
\r
645 /// The key to search for.
\r
648 /// This function is concurrency safe.
\r
651 /// A const_iterator pointing to the location of the searched for key.
\r
654 const_iterator find(const key_type& _Keyval) const
\r
656 return _Find(_Keyval);
\r
660 /// Counts the number of times a specific key appears in the container.
\r
662 /// <param name="_Keyval">
\r
663 /// The key to count.
\r
666 /// This function is concurrency safe.
\r
669 /// The number of times the key appears in the container.
\r
672 size_type count(const key_type& _Keyval) const
\r
674 size_type _Count = 0;
\r
675 const_iterator _It = _Find(_Keyval);
\r
676 for (;_It != end() && !_M_comparator(_Key_function(*_It), _Keyval); _It++)
\r
684 /// Finds the iterators pointing to the being and end locations a specific key appears in the container.
\r
686 /// <param name="_Keyval">
\r
687 /// The key to search for.
\r
690 /// This function is concurrency safe.
\r
691 /// <para>It is possible that concurrent inserts may cause the additional keys to be inserted after the
\r
692 /// begin iterator and before the end iterator.</para>
\r
695 /// A <see cref="pair Class">pair</see> where the first element is an iterator to the beginning and the second element
\r
696 /// is an iterator to the end of the range..
\r
699 _Pairii equal_range(const key_type& _Keyval)
\r
701 return _Equal_range(_Keyval);
\r
705 /// Finds the const_iterators pointing to the being and end locations a specific key appears in the container.
\r
707 /// <param name="_Keyval">
\r
708 /// The key to search for.
\r
711 /// This function is concurrency safe.
\r
712 /// <para>It is possible that concurrent inserts may cause the additional keys to be inserted after the
\r
713 /// begin iterator and before the end iterator.</para>
\r
716 /// A <see cref="pair Class">pair</see> where the first element is a const_iterator to the beginning and the second element
\r
717 /// is a const_iterator to the end of the range.
\r
720 _Paircc equal_range(const key_type& _Keyval) const
\r
722 return _Equal_range(_Keyval);
\r
725 // Bucket interface - for debugging
\r
727 /// Returns the current number of buckets in this container.
\r
730 /// The current number of buckets in this container.
\r
733 size_type unsafe_bucket_count() const
\r
735 return _M_number_of_buckets;
\r
739 /// Returns the maximum number of buckets in this container.
\r
742 /// The maximum number of buckets in this container.
\r
745 size_type unsafe_max_bucket_count() const
\r
747 return _Segment_size(_Pointers_per_table-1);
\r
751 /// Returns the number of items in a specific bucket of this container.
\r
753 /// <param name="_Bucket">
\r
754 /// The bucket to search for.
\r
757 /// The current number of buckets in this container.
\r
760 size_type unsafe_bucket_size(size_type _Bucket)
\r
762 size_type _Count = 0;
\r
764 if (!_Is_initialized(_Bucket))
\r
769 _Full_iterator _Iterator = _Get_bucket(_Bucket);
\r
772 for (; _Iterator != _M_split_ordered_list._End() && !_Iterator._Mynode()->_Is_dummy(); _Iterator++)
\r
781 /// Returns the bucket index that a specific key maps to in this container.
\r
783 /// <param name="_Keyval">
\r
784 /// The element key being searched for.
\r
787 /// The bucket index for the key in this container.
\r
790 size_type unsafe_bucket(const key_type& _Keyval) const
\r
792 _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
\r
793 size_type _Bucket = _Order_key % _M_number_of_buckets;
\r
797 // If the bucket is initialized, return a first non-dummy element in it
\r
800 /// Returns an iterator to the first element in this container for a specific bucket.
\r
802 /// <param name="_Bucket">
\r
803 /// The bucket index.
\r
806 /// An iterator pointing to the beginning of the bucket.
\r
809 local_iterator unsafe_begin(size_type _Bucket)
\r
811 // It is possible the bucket being searched for has not yet been initialized
\r
812 if (!_Is_initialized(_Bucket))
\r
814 _Initialize_bucket(_Bucket);
\r
817 _Full_iterator _Iterator = _Get_bucket(_Bucket);
\r
818 return _M_split_ordered_list._Get_first_real_iterator(_Iterator);
\r
821 // If the bucket is initialized, return a first non-dummy element in it
\r
824 /// Returns a const_iterator to the first element in this container for a specific bucket.
\r
826 /// <param name="_Bucket">
\r
827 /// The bucket index.
\r
830 /// A const_iterator pointing to the beginning of the bucket.
\r
833 const_local_iterator unsafe_begin(size_type _Bucket) const
\r
835 // It is possible the bucket being searched for has not yet been initialized
\r
836 if (!_Is_initialized(_Bucket))
\r
838 _Initialize_bucket(_Bucket);
\r
842 _Full_const_iterator _Iterator = _Get_bucket(_Bucket);
\r
843 return _M_split_ordered_list._Get_first_real_iterator(_Iterator);
\r
846 // Returns the iterator after the last non-dummy element in the bucket
\r
849 /// Returns an iterator to the last element in this container for a specific bucket.
\r
851 /// <param name="_Bucket">
\r
852 /// The bucket index.
\r
855 /// An iterator pointing to the end of the bucket.
\r
858 local_iterator unsafe_end(size_type _Bucket)
\r
860 // If we've increased the number of buckets, there's a chance the intermediate dummy
\r
861 // node marking the end of this bucket has not yet been lazily initialized.
\r
862 // Inserting from from _M_number_of_buckets/2 to _M_number_of_buckets will recursively
\r
863 // initialize all the dummy nodes in the map.
\r
864 for(size_type _Bucket_index = _M_number_of_buckets >> 1; _Bucket_index < _M_number_of_buckets; _Bucket_index++)
\r
866 if (!_Is_initialized(_Bucket_index))
\r
868 _Initialize_bucket(_Bucket_index);
\r
872 _Full_iterator _Iterator = _Get_bucket(_Bucket);
\r
874 // Find the end of the bucket, denoted by the dummy element
\r
879 while(_Iterator != _M_split_ordered_list._End() && !_Iterator._Mynode()->_Is_dummy());
\r
881 // Return the first real element past the end of the bucket
\r
882 return _M_split_ordered_list._Get_first_real_iterator(_Iterator);
\r
885 // Returns the iterator after the last non-dummy element in the bucket
\r
888 /// Returns a const_iterator to the last element in this container for a specific bucket.
\r
890 /// <param name="_Bucket">
\r
891 /// The bucket index.
\r
894 /// A const_iterator pointing to the end of the bucket.
\r
897 const_local_iterator unsafe_end(size_type _Bucket) const
\r
899 // If we've increased the number of buckets, there's a chance the intermediate dummy
\r
900 // node marking the end of this bucket has not yet been lazily initialized.
\r
901 // Inserting from from _M_number_of_buckets/2 to _M_number_of_buckets will recursively
\r
902 // initialize all the dummy nodes in the map.
\r
903 for(size_type _Bucket_index = _M_number_of_buckets >> 1; _Bucket_index < _M_number_of_buckets; _Bucket_index++)
\r
905 if (!_Is_initialized(_Bucket_index))
\r
907 _Initialize_bucket(_Bucket_index);
\r
911 _Full_const_iterator _Iterator = _Get_bucket(_Bucket);
\r
913 // Find the end of the bucket, denoted by the dummy element
\r
918 while(_Iterator != _M_split_ordered_list._End() && !_Iterator._Mynode()->_Is_dummy());
\r
920 // Return the first real element past the end of the bucket
\r
921 return _M_split_ordered_list._Get_first_real_iterator(_Iterator);
\r
925 /// Returns a const_iterator to the first element in this container for a specific bucket.
\r
927 /// <param name="_Bucket">
\r
928 /// The bucket index.
\r
931 /// A const_iterator pointing to the beginning of the bucket.
\r
934 const_local_iterator unsafe_cbegin(size_type _Bucket) const
\r
936 return ((const _Mytype *) this)->begin();
\r
940 /// Returns a const_iterator to the last element in this container for a specific bucket.
\r
942 /// <param name="_Bucket">
\r
943 /// The bucket index.
\r
946 /// A const_iterator pointing to the end of the bucket.
\r
949 const_local_iterator unsafe_cend(size_type _Bucket) const
\r
951 return ((const _Mytype *) this)->end();
\r
956 /// Computes and returns the current load factor of the container.
\r
957 /// The load factor is the number of elements in the container divided by the number of buckets.
\r
960 /// The load factor for the container
\r
963 float load_factor() const
\r
965 return (float) size() / (float) unsafe_bucket_count();
\r
969 /// Returns the maximum load factor of the container. The maximum load factor is the
\r
970 /// largest number of elements than can be in any bucket before the container grows its
\r
971 /// internal table.
\r
974 /// The maximum load factor for the container
\r
977 float max_load_factor() const
\r
979 return _M_maximum_bucket_size;
\r
983 /// Sets the maximum load factor of the container to a specific value.
\r
985 /// <param name="_Newmax">
\r
986 /// The desired load factor for this container.
\r
989 /// Throws an <see cref="out_of_range Class">out_of_range</see> exception if the load factor is invalid (NaN or negative)
\r
992 void max_load_factor(float _Newmax)
\r
994 // The _Newmax != _Newmax is a check for NaN, because NaN is != to itself
\r
995 if (_Newmax != _Newmax || _Newmax < 0)
\r
997 throw std::out_of_range("invalid hash load factor");
\r
1000 _M_maximum_bucket_size = _Newmax;
\r
1003 // This function is a noop, because the underlying split-ordered list
\r
1004 // is already sorted, so an increase in the bucket number will be
\r
1005 // reflected next time this bucket is touched.
\r
1008 /// Sets the the number of buckets for the container to a specific value.
\r
1010 /// <param name="_Buckets">
\r
1011 /// The desired number of buckets.
\r
1014 /// The number of buckets must be a power of 2. If not a power of 2, it will be rounded up to
\r
1015 /// the next largest power of 2.
\r
1016 /// <para>Throws an <see cref="out_of_range Class">out_of_range</see> exception if the number of buckets
\r
1017 /// is invalid (0 or greater than the maximum number of buckets)</para>
\r
1020 void rehash(size_type _Buckets)
\r
1022 size_type _Current_buckets = _M_number_of_buckets;
\r
1024 if (_Current_buckets > _Buckets)
\r
1028 else if (_Buckets <= 0 || _Buckets > unsafe_max_bucket_count())
\r
1030 throw std::out_of_range("invalid number of buckets");
\r
1032 // Round up the number of buckets to the next largest power of 2
\r
1033 _M_number_of_buckets = ((size_type) 1) << _Get_msb(_Buckets*2-1);
\r
1038 // Initialize the hash and keep the first bucket open
\r
1041 // Allocate an array of segment pointers
\r
1042 memset(_M_buckets, 0, _Pointers_per_table * sizeof(void *));
\r
1044 // Insert the first element in the split-ordered list
\r
1045 _Full_iterator _Dummy_node = _M_split_ordered_list._Begin();
\r
1046 _Set_bucket(0, _Dummy_node);
\r
1049 void _Copy(const _Mytype& _Right)
\r
1053 _M_maximum_bucket_size = _Right._M_maximum_bucket_size;
\r
1054 _M_number_of_buckets = _Right._M_number_of_buckets;
\r
1058 insert(_Right.begin(), _Right.end());
\r
1059 _M_comparator = _Right._M_comparator;
\r
1063 _M_split_ordered_list.clear();
\r
1068 void _Swap_buckets(_Concurrent_hash& _Right)
\r
1070 if (_M_allocator == _Right._M_allocator)
\r
1072 // Swap all node segments
\r
1073 for (size_type _Index = 0; _Index < _Pointers_per_table; _Index++)
\r
1075 _Full_iterator * _Iterator_pointer = _M_buckets[_Index];
\r
1076 _M_buckets[_Index] = _Right._M_buckets[_Index];
\r
1077 _Right._M_buckets[_Index] = _Iterator_pointer;
\r
1082 throw std::invalid_argument("swap is invalid on non-equal allocators");
\r
1087 size_type _Distance(const_iterator _First, const_iterator _Last) const
\r
1089 size_type _Num = 0;
\r
1091 for (const_iterator _Iterator = _First; _Iterator != _Last; _Iterator++)
\r
1099 // Insert an element in the hash given its value
\r
1100 template<typename _ValTy>
\r
1101 _Pairib _Insert(_ValTy&& _Value)
\r
1103 _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Key_function(_Value));
\r
1104 size_type _Bucket = _Order_key % _M_number_of_buckets;
\r
1106 // If bucket is empty, initialize it first
\r
1107 if (!_Is_initialized(_Bucket))
\r
1109 _Initialize_bucket(_Bucket);
\r
1113 _Order_key = _Split_order_regular_key(_Order_key);
\r
1114 _Full_iterator _Iterator = _Get_bucket(_Bucket);
\r
1115 _Full_iterator _Last = _M_split_ordered_list._End();
\r
1116 _Full_iterator _Where = _Iterator;
\r
1117 _Nodeptr _New_node = _M_split_ordered_list._Buynode(_Order_key, std::forward<_ValTy>(_Value));
\r
1119 _ASSERT_EXPR(_Where != _Last, L"Invalid head node");
\r
1121 // First node is a dummy node
\r
1126 if (_Where == _Last || _Mylist::_Get_key(_Where) > _Order_key)
\r
1128 // Try to insert it in the right place
\r
1129 _Pairib _Result = _M_split_ordered_list._Insert(_Iterator, _Where, _New_node, &_New_count);
\r
1131 if (_Result.second)
\r
1133 // Insertion succeeded, adjust the table size, if needed
\r
1134 _Adjust_table_size(_New_count, _M_number_of_buckets);
\r
1139 // Insertion failed: either the same node was inserted by another thread, or
\r
1140 // another element was inserted at exactly the same place as this node.
\r
1141 // Proceed with the search from the previous location where order key was
\r
1142 // known to be larger (note: this is legal only because there is no safe
\r
1143 // concurrent erase operation supported).
\r
1144 _Where = _Iterator;
\r
1149 else if (!_M_allow_multimapping && _Mylist::_Get_key(_Where) == _Order_key &&
\r
1150 _M_comparator(_Key_function(*_Where), _Key_function(_New_node->_M_element)) == 0)
\r
1152 // If the insert failed (element already there), then delete the new one
\r
1153 _M_split_ordered_list._Erase(_New_node);
\r
1155 // Element already in the list, return it
\r
1156 return _Pairib(_M_split_ordered_list._Get_iterator(_Where), false);
\r
1159 // Move the iterator forward
\r
1160 _Iterator = _Where;
\r
1164 // Find the element in the split-ordered list
\r
1165 iterator _Find(const key_type& _Keyval)
\r
1167 _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
\r
1168 size_type _Bucket = _Order_key % _M_number_of_buckets;
\r
1170 // If _Bucket is empty, initialize it first
\r
1171 if (!_Is_initialized(_Bucket))
\r
1173 _Initialize_bucket(_Bucket);
\r
1176 _Order_key = _Split_order_regular_key(_Order_key);
\r
1177 _Full_iterator _Last = _M_split_ordered_list._End();
\r
1179 for (_Full_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++)
\r
1181 if (_Mylist::_Get_key(_Iterator) > _Order_key)
\r
1183 // If the order key is smaller than the current order key, the element
\r
1184 // is not in the hash.
\r
1187 else if (_Mylist::_Get_key(_Iterator) == _Order_key)
\r
1189 // The fact that order keys match does not mean that the element is found.
\r
1190 // Key function comparison has to be performed to check whether this is the
\r
1191 // right element. If not, keep searching while order key is the same.
\r
1192 if (!_M_comparator(_Key_function(*_Iterator), _Keyval))
\r
1194 return _M_split_ordered_list._Get_iterator(_Iterator);
\r
1202 // Find the element in the split-ordered list
\r
1203 const_iterator _Find(const key_type& _Keyval) const
\r
1205 _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
\r
1206 size_type _Bucket = _Order_key % _M_number_of_buckets;
\r
1208 // If _Bucket has not been initialized, keep searching up for a parent bucket
\r
1209 // that has been initialized. Worst case is the entire map will be read.
\r
1210 while (!_Is_initialized(_Bucket))
\r
1212 _Bucket = _Get_parent(_Bucket);
\r
1215 _Order_key = _Split_order_regular_key(_Order_key);
\r
1216 _Full_const_iterator _Last = _M_split_ordered_list._End();
\r
1218 for (_Full_const_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++)
\r
1220 if (_Mylist::_Get_key(_Iterator) > _Order_key)
\r
1222 // If the order key is smaller than the current order key, the element
\r
1223 // is not in the hash.
\r
1226 else if (_Mylist::_Get_key(_Iterator) == _Order_key)
\r
1228 // The fact that order keys match does not mean that the element is found.
\r
1229 // Key function comparison has to be performed to check whether this is the
\r
1230 // right element. If not, keep searching while order key is the same.
\r
1231 if (!_M_comparator(_Key_function(*_Iterator), _Keyval))
\r
1233 return _M_split_ordered_list._Get_iterator(_Iterator);
\r
1241 // Erase an element from the list. This is not a concurrency safe function.
\r
1242 iterator _Erase(const_iterator _Iterator)
\r
1244 key_type _Keyval = _Key_function(*_Iterator);
\r
1245 _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
\r
1246 size_type _Bucket = _Order_key % _M_number_of_buckets;
\r
1248 // If bucket is empty, initialize it first
\r
1249 if (!_Is_initialized(_Bucket))
\r
1251 _Initialize_bucket(_Bucket);
\r
1254 _Order_key = _Split_order_regular_key(_Order_key);
\r
1256 _Full_iterator _Previous = _Get_bucket(_Bucket);
\r
1257 _Full_iterator _Last = _M_split_ordered_list._End();
\r
1258 _Full_iterator _Where = _Previous;
\r
1260 _ASSERT_EXPR(_Where != _Last, L"Invalid head node");
\r
1262 // First node is a dummy node
\r
1267 if (_Where == _Last)
\r
1271 else if (_M_split_ordered_list._Get_iterator(_Where) == _Iterator)
\r
1273 return _M_split_ordered_list._Erase(_Previous, _Iterator);
\r
1276 // Move the iterator forward
\r
1277 _Previous = _Where;
\r
1282 // Return the [begin, end) pair of iterators with the same key values.
\r
1283 // This operation makes sense only if mapping is many-to-one.
\r
1284 _Pairii _Equal_range(const key_type& _Keyval)
\r
1286 _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
\r
1287 size_type _Bucket = _Order_key % _M_number_of_buckets;
\r
1289 // If _Bucket is empty, initialize it first
\r
1290 if (!_Is_initialized(_Bucket))
\r
1292 _Initialize_bucket(_Bucket);
\r
1295 _Order_key = _Split_order_regular_key(_Order_key);
\r
1296 _Full_iterator _Last = _M_split_ordered_list._End();
\r
1298 for (_Full_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++)
\r
1300 if (_Mylist::_Get_key(_Iterator) > _Order_key)
\r
1302 // There is no element with the given key
\r
1303 return _Pairii(end(), end());
\r
1305 else if (_Mylist::_Get_key(_Iterator) == _Order_key && !_M_comparator(_Key_function(*_Iterator), _Keyval))
\r
1307 iterator _Begin = _M_split_ordered_list._Get_iterator(_Iterator);
\r
1308 iterator _End= _Begin;
\r
1310 for (;_End != end() && !_M_comparator(_Key_function(*_End), _Keyval); _End++)
\r
1314 return _Pairii(_Begin, _End);
\r
1318 return _Pairii(end(), end());
\r
1321 // Return the [begin, end) pair of const iterators with the same key values.
\r
1322 // This operation makes sense only if mapping is many-to-one.
\r
1323 _Paircc _Equal_range(const key_type& _Keyval) const
\r
1325 _Split_order_key _Order_key = (_Split_order_key) _M_comparator(_Keyval);
\r
1326 size_type _Bucket = _Order_key % _M_number_of_buckets;
\r
1328 // If _Bucket has not been initialized, keep searching up for a parent bucket
\r
1329 // that has been initialized. Worst case is the entire map will be read.
\r
1330 while (!_Is_initialized(_Bucket))
\r
1332 _Bucket = _Get_parent(_Bucket);
\r
1335 _Order_key = _Split_order_regular_key(_Order_key);
\r
1336 _Full_const_iterator _Last = _M_split_ordered_list._End();
\r
1338 for (_Full_const_iterator _Iterator = _Get_bucket(_Bucket); _Iterator != _Last; _Iterator++)
\r
1340 if (_Mylist::_Get_key(_Iterator) > _Order_key)
\r
1342 // There is no element with the given key
\r
1343 return _Paircc(end(), end());
\r
1345 else if (_Mylist::_Get_key(_Iterator) == _Order_key && !_M_comparator(_Key_function(*_Iterator), _Keyval))
\r
1347 const_iterator _Begin = _M_split_ordered_list._Get_iterator(_Iterator);
\r
1348 const_iterator _End = _Begin;
\r
1350 for (; _End != end() && !_M_comparator(_Key_function(*_End), _Keyval); _End++)
\r
1354 return _Paircc(_Begin, _End);
\r
1358 return _Paircc(end(), end());
\r
1362 void _Initialize_bucket(size_type _Bucket)
\r
1364 // Bucket 0 has no parent. Initialize it and return.
\r
1371 size_type _Parent_bucket = _Get_parent(_Bucket);
\r
1373 // All _Parent_bucket buckets have to be initialized before this bucket is
\r
1374 if (!_Is_initialized(_Parent_bucket))
\r
1376 _Initialize_bucket(_Parent_bucket);
\r
1379 _Full_iterator _Parent = _Get_bucket(_Parent_bucket);
\r
1381 // Create a dummy first node in this bucket
\r
1382 _Full_iterator _Dummy_node = _M_split_ordered_list._Insert_dummy(_Parent, _Split_order_dummy_key(_Bucket));
\r
1383 _Set_bucket(_Bucket, _Dummy_node);
\r
1386 void _Adjust_table_size(size_type _Total_elements, size_type _Current_size)
\r
1388 // Grow the table by a factor of 2 if possible and needed
\r
1389 if (((float) _Total_elements / (float) _Current_size) > _M_maximum_bucket_size)
\r
1391 // Double the size of the hash only if size has not changed inbetween loads
\r
1392 _InterlockedCompareExchangeSizeT(&_M_number_of_buckets, 2 * _Current_size, _Current_size);
\r
1396 size_type _Get_parent(size_type _Bucket) const
\r
1398 // Unsets bucket's most significant turned-on bit
\r
1399 unsigned char _Msb = _Get_msb(_Bucket);
\r
1400 return _Bucket & ~(1 << _Msb);
\r
1404 // Dynamic sized array (segments)
\r
1406 _Full_iterator _Get_bucket(size_type _Bucket) const
\r
1408 size_type _Segment = _Segment_index_of(_Bucket);
\r
1409 _Bucket -= _Segment_base(_Segment);
\r
1410 return _M_buckets[_Segment][_Bucket];
\r
1413 void _Set_bucket(size_type _Bucket, _Full_iterator _Dummy_head)
\r
1415 size_type _Segment = _Segment_index_of(_Bucket);
\r
1416 _Bucket -= _Segment_base(_Segment);
\r
1418 if (_M_buckets[_Segment] == NULL)
\r
1420 size_type _Seg_size = _Segment_size(_Segment);
\r
1421 _Full_iterator * _New_segment = _M_allocator.allocate(_Seg_size);
\r
1422 _Uninitialized_default_fill_n(_New_segment, _Seg_size, (_Full_iterator *) 0, _M_allocator);
\r
1423 if (_InterlockedCompareExchangePointer((void * volatile *) &_M_buckets[_Segment], _New_segment, NULL) != NULL)
\r
1425 _M_allocator.deallocate(_New_segment, _Seg_size);
\r
1428 _M_buckets[_Segment][_Bucket] = _Dummy_head;
\r
1431 bool _Is_initialized(size_type _Bucket) const
\r
1433 size_type _Segment = _Segment_index_of(_Bucket);
\r
1434 _Bucket -= _Segment_base(_Segment);
\r
1436 if (_M_buckets[_Segment] == NULL)
\r
1441 _Full_iterator _Iterator = _M_buckets[_Segment][_Bucket];
\r
1442 return (_Iterator._Mynode() != NULL);
\r
1445 // Utilities for keys
\r
1447 _Split_order_key _Reverse(_Map_key _Order_key) const
\r
1449 _Split_order_key _Reversed_order_key;
\r
1451 unsigned char * _Original = (unsigned char *) &_Order_key;
\r
1452 unsigned char * _Reversed = (unsigned char *) &_Reversed_order_key;
\r
1454 int _Size = sizeof(_Map_key);
\r
1455 for (int _Index = 0; _Index < _Size; _Index++)
\r
1457 _Reversed[_Size - _Index - 1] = _Reverse_byte(_Original[_Index]);
\r
1460 return _Reversed_order_key;
\r
1463 // A regular order key has its original hash value reversed and the last bit set
\r
1464 _Split_order_key _Split_order_regular_key(_Map_key _Order_key) const
\r
1466 return _Reverse(_Order_key) | 0x1;
\r
1469 // A dummy order key has its original hash value reversed and the last bit unset
\r
1470 _Split_order_key _Split_order_dummy_key(_Map_key _Order_key) const
\r
1472 return _Reverse(_Order_key) & ~(0x1);
\r
1475 // Shared variables
\r
1476 _Full_iterator * _M_buckets[_Pointers_per_table]; // The segment table
\r
1477 _Mylist _M_split_ordered_list; // List where all the elements are kept
\r
1478 typename allocator_type::template rebind<_Full_iterator>::other _M_allocator; // Allocator object for segments
\r
1479 size_type _M_number_of_buckets; // Current table size
\r
1480 float _M_maximum_bucket_size; // Maximum size of the bucket
\r
1483 #pragma warning(pop) // warning 4127 -- while (true) has a constant expression in it
\r
1485 } // namespace details;
\r
1486 } // namespace Concurrency
\r