BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bslalg_hashtableimputil.h
Go to the documentation of this file.
1/// @file bslalg_hashtableimputil.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bslalg_hashtableimputil.h -*-C++-*-
8#ifndef INCLUDED_BSLALG_HASHTABLEIMPUTIL
9#define INCLUDED_BSLALG_HASHTABLEIMPUTIL
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bslalg_hashtableimputil bslalg_hashtableimputil
15/// @brief Provide algorithms for implementing a hash table.
16/// @addtogroup bsl
17/// @{
18/// @addtogroup bslalg
19/// @{
20/// @addtogroup bslalg_hashtableimputil
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bslalg_hashtableimputil-purpose"> Purpose</a>
25/// * <a href="#bslalg_hashtableimputil-classes"> Classes </a>
26/// * <a href="#bslalg_hashtableimputil-description"> Description </a>
27/// * <a href="#bslalg_hashtableimputil-hash-table-structure"> Hash Table Structure </a>
28/// * <a href="#bslalg_hashtableimputil-hash-function-and-the-adjusted-hash-value"> Hash Function and the Adjusted Hash Value </a>
29/// * <a href="#bslalg_hashtableimputil-well-formed-hashtableanchor-objects"> Well-Formed HashTableAnchor Objects </a>
30/// * <a href="#bslalg_hashtableimputil-key_config-template-parameter"> KEY_CONFIG Template Parameter </a>
31/// * <a href="#bslalg_hashtableimputil-key_config"> KEY_CONFIG </a>
32/// * <a href="#bslalg_hashtableimputil-usage"> Usage </a>
33/// * <a href="#bslalg_hashtableimputil-example-1-creating-and-using-a-hash-set"> Example 1: Creating and Using a Hash Set </a>
34///
35/// # Purpose {#bslalg_hashtableimputil-purpose}
36/// Provide algorithms for implementing a hash table.
37///
38/// # Classes {#bslalg_hashtableimputil-classes}
39///
40/// - bslalg::HashTableImpUtil: functions used to implement a hash table
41///
42/// @see bslalg_bidirectionallinklistutil, bslalg_hashtableanchor,
43/// bslstl_hashtable
44///
45/// # Description {#bslalg_hashtableimputil-description}
46/// This component provides a namespace for utility functions used
47/// to implement a hash table container. Almost all the functions provided by
48/// this component operate on a `HashTableAnchor`, a type encapsulating the key
49/// data members of a hash table.
50///
51/// ## Hash Table Structure {#bslalg_hashtableimputil-hash-table-structure}
52///
53///
54/// The utilities provided by this component are used to create and manipulate
55/// a hash table that resolves collisions using a linked-list of elements
56/// (i.e., chaining). Many of the operations provided by `HashTableImpUtil`
57/// operate on a `HashTableAnchor`, which encapsulates the key data members of a
58/// hash table. A `HashTableAnchor` has the address of a single, doubly linked
59/// list holding all the elements in the hash table, as well as the address of
60/// an array of buckets. Each bucket holds a reference to the first and last
61/// element in the linked-list whose *adjusted* *hash* *value* is equal to the
62/// index of the bucket. Further, the functions in this component ensure (and
63/// require) that all elements that fall within a bucket form a contiguous
64/// sequence in the linked list, as can be seen in the
65/// diagram below:
66/// @code
67/// FIG 1: a hash table holding 5 elements
68///
69/// Hash Function: h(n) -> n [identity function]
70/// F: First Element
71/// L: Last Element
72///
73/// 0 1 2 3 4
74/// +-------+-------+-------+-------+-------+--
75/// bucket array | F L | F L | F L | F L | F L | ...
76/// +--+-+--+-------+-------+--+-+--+-------+--
77/// | \___ _________/ /
78/// \ \ / |
79/// V V V V
80/// ,-. ,-. ,-. ,-. ,-.
81/// doubly |---|0|---|0|---|3|---|3|---|3|--|
82/// linked-list `-' `-' `-' `-' `-'
83/// @endcode
84///
85/// ## Hash Function and the Adjusted Hash Value {#bslalg_hashtableimputil-hash-function-and-the-adjusted-hash-value}
86///
87///
88/// The C++11 standard defines a hash function as a function `h(k)` returning
89/// (integral) values of type `size_t`, such that, for two different values of
90/// `k1` and `k2`, the probability that `h(k1) == h(k2)` is true should approach
91/// `1.0 / numeric_limits<size_t>::max()` (see 17.6.3.4 [hash.requirements]).
92/// Such a function `h(k)` may return values within the entire range of values
93/// that can be described using `size_t`, [0 .. numeric_limits<size_t>::max()],
94/// however the array of buckets maintained by a hash table is typically
95/// significantly smaller than `number_limits<size_t>::max()`, therefore a
96/// hash-table implementation must adjust the returned hash function so that it
97/// falls in the valid range of bucket indices (typically either using an
98/// integer division or modulo operation) -- we refer to this as the *adjusted*
99/// *hash* *value*. Note that currently `HashTableImpUtil` adjusts the value
100/// returned by a supplied hash function using `operator%` (modulo), which
101/// is more resilient to pathological behaviors when used in conjunction with a
102/// hash function that may produce contiguous hash values (with the `div` method
103/// lower order bits do not participate to the final adjusted value); however,
104/// the means of adjustment may change in the future.
105///
106/// ## Well-Formed HashTableAnchor Objects {#bslalg_hashtableimputil-well-formed-hashtableanchor-objects}
107///
108///
109/// Many of the algorithms defined in this component operate on
110/// `HashTableAnchor` objects, which describe the attributes of a hash table.
111/// The `HashTableAnchor` objects supplied to `HashTableImpUtil` are required
112/// to meet a series of constraints that are not enforced by the
113/// `HashTableAnchor` type itself. A `HashTableAnchor` object meeting these
114/// requirements is said to be **well-formed** and the method
115/// `HashTableImpUtil::isWellFormed` returns `true` for such an object. A
116/// `HastTableAnchor` is considered well-formed for a particular key policy,
117/// `KEY_CONFIG`, and hash functor, `HASHER`, if all of the following are true:
118///
119/// 1. The list refers to a well-formed doubly linked list (see
120/// @ref bslalg_bidirectionallinklistutil ).
121/// 2. Each link in the list is an object of type
122/// `BidirectionalNode<KEY_CONFIG::ValueType>`
123/// 3. For each bucket, the range of nodes `[ bucket.first(), bucket.last() ]`
124/// contains all nodes in the hash table for which
125/// `computeBucketIndex(HASHER(extractKey(link)` is the index of the bucket,
126/// and no other nodes.
127///
128/// ## KEY_CONFIG Template Parameter {#bslalg_hashtableimputil-key_config-template-parameter}
129///
130///
131/// Several of the operations provided by `HashTableImpUtil` are template
132/// functions parameterized on the typename `KEY_CONFIG`.
133///
134/// ### KEY_CONFIG {#bslalg_hashtableimputil-key_config}
135///
136///
137/// The `KEY_CONFIG` template parameter must provide the following type aliases
138/// and functions:
139/// @code
140/// /// Alias for the type of the values stored by the `BidirectionalNode`
141/// /// elements in the hash table.
142/// typedef <VALUE_TYPE> ValueType;
143///
144/// /// Alias for the type of the key value extracted from the `ValueType`
145/// /// stored in the `BidirectionalNode` elements of a hash table.
146/// typedef <KEY_TYPE> KeyType;
147///
148/// /// Return the `KeyType` information associated with the specified
149/// /// `object`.
150/// static const KeyType& extractKey(const ValueType& obj);
151/// @endcode
152///
153/// ## Usage {#bslalg_hashtableimputil-usage}
154///
155///
156/// This section illustrates intended usage of this component.
157///
158/// ### Example 1: Creating and Using a Hash Set {#bslalg_hashtableimputil-example-1-creating-and-using-a-hash-set}
159///
160///
161/// Suppose we want to build a hash set that will keep track of keys stored in
162/// set.
163///
164/// First, we define an abstract template class `HashSet` that will provide a
165/// hash set for any type that has a copy constructor, a destructor, an equality
166/// comparator and a hash function. We inherit from the `HashTableAnchor` class
167/// use the `BidirectionalLinkListUtil` and `HashTableImpUtil` classes to
168/// facilitate building the table:
169/// @code
170/// template <class KEY, class HASHER, class EQUAL>
171/// class HashSet : public bslalg::HashTableAnchor {
172/// // PRIVATE TYPES
173/// typedef bslalg::BidirectionalLink Link;
174/// typedef bslalg::BidirectionalNode<KEY> Node;
175/// typedef bslalg::HashTableBucket Bucket;
176/// typedef bslalg::BidirectionalLinkListUtil ListUtil;
177/// typedef bslalg::HashTableImpUtil ImpUtil;
178/// typedef std::size_t size_t;
179///
180/// struct Policy {
181/// typedef KEY KeyType;
182/// typedef KEY ValueType;
183///
184/// static const KeyType& extractKey(const ValueType& value)
185/// {
186/// return value;
187/// }
188/// };
189///
190/// // DATA
191/// double d_maxLoadFactor;
192/// unsigned d_numNodes;
193/// HASHER d_hasher;
194/// EQUAL d_equal;
195/// bslma::Allocator *d_allocator_p;
196///
197/// // PRIVATE MANIPULATORS
198///
199/// /// Roughly double the number of buckets, such that the number of
200/// /// buckets shall always be `2^N - 1`.
201/// void grow();
202///
203/// // PRIVATE ACCESSORS
204///
205/// /// Perform sanity checks on this table, returning 'true' if all the
206/// /// tests pass and 'false' otherwise. Note that many of the checks
207/// /// are done with the 'ASSERTV' macro and will cause messages to be
208/// /// written to the console.
209/// bool checkInvariants() const;
210///
211/// /// Return a pointer to the node containing the specified 'key', and
212/// /// 0 if no such node is in the table.
213/// Node* find(const KEY& key, size_t hashCode) const;
214///
215/// private:
216/// // NOT IMPLEMENTED
217/// HashSet(const HashSet&, bslma::Allocator *);
218/// HashSet& operator=(const HashSet&);
219///
220/// public:
221/// // CREATORS
222///
223/// /// Create a `HashSet`, using the specified `allocator`. If no
224/// /// allocator is specified, use the default allocator.
225/// explicit
226/// HashSet(bslma::Allocator *allocator = 0);
227///
228/// // Destroy this `HashSet`, freeing all its memory.
229/// ~HashSet();
230///
231/// // MANIPULATORS
232///
233/// /// If the specfied `key` is not in this hash table, add it,
234/// /// returning 'true'. If it is already in the table, return `false`
235/// /// with no action taken.
236/// bool insert(const KEY& key);
237///
238/// /// If the specfied `key` is in this hash table, remove it,
239/// /// returning `true`. If it is not found in the table, return
240/// /// `false` with no action taken.
241/// bool erase(const KEY& key);
242///
243/// // ACCESSORS
244///
245/// /// Return 1 if the specified `key` is in this table and 0
246/// /// otherwise.
247/// std::size_t count(const KEY& key) const;
248///
249/// /// Return the number of discrete keys that are stored in this
250/// /// table.
251/// std::size_t size() const;
252/// };
253///
254/// // PRIVATE MANIPULATORS
255/// template <class KEY, class HASHER, class EQUAL>
256/// void HashSet<KEY, HASHER, EQUAL>::grow()
257/// {
258/// // `bucketArraySize` will always be `2^N - 1`, so that if hashed values
259/// // are aligned by some 2^N they're likely to be relatively prime to the
260/// // length of the hash table.
261///
262/// d_allocator_p->deallocate(bucketArrayAddress());
263/// size_t newBucketArraySize = bucketArraySize() * 2 + 1;
264/// setBucketArrayAddressAndSize((Bucket *) d_allocator_p->allocate(
265/// newBucketArraySize * sizeof(Bucket)),
266/// newBucketArraySize);
267///
268/// ImpUtil::rehash<Policy, HASHER>(this,
269/// listRootAddress(),
270/// d_hasher);
271/// }
272///
273/// // PRIVATE ACCESSORS
274/// template <class KEY, class HASHER, class EQUAL>
275/// bool HashSet<KEY, HASHER, EQUAL>::checkInvariants() const
276/// {
277/// @endcode
278/// `HashTableImpUtil`s `isWellFormed` will verify that all nodes are in their
279/// proper buckets, that there are no buckets containing nodes that are not in
280/// the main linked list, and no nodes in the main linked list that are not in
281/// buckets. To verify that `d_numNodes` is correct we have to traverse the
282/// list and count the nodes ourselves.
283/// @code
284/// size_t numNodes = 0;
285/// for (BidirectionalLink *cursor = listRootAddress;
286/// cursor; cursor = cursor->nextLink()) {
287/// ++numNodes;
288/// }
289///
290/// return size() == numNodes &&
291/// ImpUtil::isWellFormed<Policy, HASHER>(this, d_allocator_p);
292/// }
293///
294/// template <class KEY, class HASHER, class EQUAL>
295/// bslalg::BidirectionalNode<KEY> *HashSet<KEY, HASHER, EQUAL>::find(
296/// const KEY& key,
297/// std::size_t hashCode) const
298/// {
299/// return (Node *) ImpUtil::find<Policy, EQUAL>(*this,
300/// key,
301/// d_equal,
302/// hashCode);
303/// }
304///
305/// // CREATORS
306/// template <class KEY, class HASHER, class EQUAL>
307/// HashSet<KEY, HASHER, EQUAL>::HashSet(bslma::Allocator *allocator)
308/// : HashTableAnchor(0, 0, 0)
309/// , d_maxLoadFactor(0.4)
310/// , d_numNodes(0)
311/// {
312/// enum { NUM_BUCKETS = 3 }; // 'NUM_BUCKETS' must be '2^N - 1' for
313/// // some 'N'.
314///
315/// d_allocator_p = bslma::Default::allocator(allocator);
316/// std::size_t bucketArraySizeInBytes = NUM_BUCKETS * sizeof(Bucket);
317/// setBucketArrayAddressAndSize(
318/// (Bucket *) d_allocator_p->allocate(bucketArraySizeInBytes),
319/// NUM_BUCKETS);
320/// memset(bucketArrayAddress(), 0, bucketArraySizeInBytes);
321/// }
322///
323/// template <class KEY, class HASHER, class EQUAL>
324/// HashSet<KEY, HASHER, EQUAL>::~HashSet()
325/// {
326/// BSLS_ASSERT_SAFE(checkInvariants());
327///
328/// for (Link *link = listRootAddress(); link; ) {
329/// Node *toDelete = (Node *) link;
330/// link = link->nextLink();
331///
332/// toDelete->value().~KEY();
333/// d_allocator_p->deallocate(toDelete);
334/// }
335///
336/// d_allocator_p->deallocate(bucketArrayAddress());
337/// }
338///
339/// // MANIPULATORS
340/// template <class KEY, class HASHER, class EQUAL>
341/// bool HashSet<KEY, HASHER, EQUAL>::erase(const KEY& key)
342/// {
343/// size_t hashCode = d_hasher(key);
344/// Node *node = find(key, hashCode);
345///
346/// if (!node) {
347/// return false; // RETURN
348/// }
349///
350/// size_t bucketIdx = ImpUtil::computeBucketIndex(hashCode,
351/// bucketArraySize());
352/// Bucket& bucket = bucketArrayAddress()[bucketIdx];
353///
354/// BSLS_ASSERT_SAFE(bucket.first() && bucket.last());
355///
356/// if (bucket.first() == node) {
357/// if (bucket.last() == node) {
358/// bucket.reset();
359/// }
360/// else {
361/// bucket.setFirst(node->nextLink());
362/// }
363/// }
364/// else if (bucket.last() == node) {
365/// bucket.setLast(node->previousLink());
366/// }
367///
368/// if (listRootAddress() == node) {
369/// setListRootAddress(node->nextLink());
370/// }
371///
372/// ListUtil::unlink(node);
373///
374/// node->value().~KEY();
375/// d_allocator_p->deallocate(node);
376///
377/// --d_numNodes;
378/// BSLS_ASSERT_SAFE(checkInvariants());
379///
380/// return true;
381/// }
382///
383/// template <class KEY, class HASHER, class EQUAL>
384/// bool HashSet<KEY, HASHER, EQUAL>::insert(const KEY& key)
385/// {
386/// size_t hashCode = d_hasher(key);
387///
388/// if (find(key, hashCode)) {
389/// // Already in set, do nothing.
390///
391/// return false; // RETURN
392/// }
393///
394/// if (bucketArraySize() * d_maxLoadFactor < d_numNodes + 1) {
395/// grow();
396/// }
397///
398/// ++d_numNodes;
399/// Node *node = (Node *) d_allocator_p->allocate(sizeof(Node));
400/// bslalg::ScalarPrimitives::copyConstruct(&node->value(),
401/// key,
402/// d_allocator_p);
403///
404/// ImpUtil::insertAtBackOfBucket(this, node, hashCode);
405///
406/// BSLS_ASSERT_SAFE(find(key, hashCode));
407/// BSLS_ASSERT_SAFE(checkInvariants());
408///
409/// return true;
410/// }
411///
412/// // ACCESSORS
413/// template <class KEY, class HASHER, class EQUAL>
414/// std::size_t HashSet<KEY, HASHER, EQUAL>::count(const KEY& key) const
415/// {
416/// return 0 != find(key, d_hasher(key));
417/// }
418///
419/// template <class KEY, class HASHER, class EQUAL>
420/// std::size_t HashSet<KEY, HASHER, EQUAL>::size() const
421/// {
422/// return d_numNodes;
423/// }
424/// @endcode
425/// Then, we customize our table to manipulate zero-terminated `const char *`
426/// strings. We make the simplifying assumption that the strings pointed at by
427/// the `const char *`s are longer-lived that the `HashSet` will be. We must
428/// provide an equality comparator so that two copies, in different locations,
429/// of the same sequence of characters will evaluate equal:
430/// @code
431/// struct StringEqual {
432/// bool operator()(const char *lhs, const char *rhs) const
433/// {
434/// return !strcmp(lhs, rhs);
435/// }
436/// };
437/// @endcode
438/// Next, we must provide a string hash function to convert a `const char *` to
439/// a `size_t`:
440/// @code
441/// struct StringHash {
442/// std::size_t operator()(const char *string) const;
443/// };
444///
445/// std::size_t StringHash::operator()(const char *string) const
446/// {
447/// enum { BITS_IN_SIZE_T = sizeof(size_t) * 8 };
448///
449/// std::size_t result = 0;
450/// for (int shift = 0; *string;
451/// ++string, shift = (shift + 7) % BITS_IN_SIZE_T) {
452/// unsigned char c = *string;
453/// if (shift <= BITS_IN_SIZE_T - 8) {
454/// result += c << shift;
455/// }
456/// else {
457/// result += c << shift;
458/// result += c >> (BITS_IN_SIZE_T - shift);
459/// }
460/// }
461///
462/// return result;
463/// };
464/// @endcode
465/// Then, we declare a couple of `TestAllocator`s to use during our example:
466/// @code
467/// bslma::TestAllocator da("defaultAllocator");
468/// bslma::DefaultAllocatorGuard defaultGuard(&da);
469///
470/// bslma::TestAllocator ta("testAllocator");
471/// @endcode
472/// Next, in `main`, we create an instance of our `HashSet` type, configured to
473/// contain `const char *` strings:
474/// @code
475/// HashSet<const char *, StringHash, StringEqual> hs(&ta);
476/// @endcode
477/// Then, we insert a few values:
478/// @code
479/// assert(1 == hs.insert("woof"));
480/// assert(1 == hs.insert("arf"));
481/// assert(1 == hs.insert("meow"));
482/// @endcode
483/// Next, we attempt to insert a redundant value, and observe that the `insert`
484/// method returns `false` to indicate that the insert was refused:
485/// @code
486/// assert(0 == hs.insert("woof"));
487/// @endcode
488/// Then, we use to `size` method to observe that there are 3 strings stored in
489/// our `HashSet`:
490/// @code
491/// assert(3 == hs.size());
492/// @endcode
493/// Next, we use the `count` method to observe, specifically, which strings are
494/// and are not in our `HashSet`:
495/// @code
496/// assert(1 == hs.count("woof"));
497/// assert(1 == hs.count("arf"));
498/// assert(1 == hs.count("meow"));
499/// assert(0 == hs.count("ruff"));
500/// assert(0 == hs.count("chomp"));
501/// @endcode
502/// Then, we attempt to erase a string which is not in our `HashSet` and observe
503/// that `false` is returned, which tells us the `erase` attempt was
504/// unsuccessful:
505/// @code
506/// assert(0 == hs.erase("ruff"));
507/// @endcode
508/// Next, we erase the string "meow", which is stored in our `HashSet` and
509/// observe that `true` is returned, telling us the `erase` attempt succeeded:
510/// @code
511/// assert(1 == hs.erase("meow"));
512/// @endcode
513/// Now, we use the `size` method to verify there are 2 strings remaining in our
514/// `HashSet`:
515/// @code
516/// assert(2 == hs.size());
517/// @endcode
518/// Finally, we use the `count` method to observe specifically which strings are
519/// still in our `HashSet`. Note that "meow" is no longer there. We observe
520/// that the default allocator was never used. When we leave the block, our
521/// `HashSet` will be destroyed, freeing its memory, then our `TestAllocator`
522/// will be destroyed, verifying that our destructor worked correctly and that
523/// no memory was leaked:
524/// @code
525/// assert(1 == hs.count("woof"));
526/// assert(1 == hs.count("arf"));
527/// assert(0 == hs.count("meow"));
528/// assert(0 == hs.count("ruff"));
529/// assert(0 == hs.count("chomp"));
530///
531/// assert(0 == da.numAllocations());
532/// @endcode
533/// @}
534/** @} */
535/** @} */
536
537/** @addtogroup bsl
538 * @{
539 */
540/** @addtogroup bslalg
541 * @{
542 */
543/** @addtogroup bslalg_hashtableimputil
544 * @{
545 */
546
547#include <bslscm_version.h>
548
554
555#include <bslma_allocator.h>
557#include <bslma_default.h>
558
559#include <bslmf_conditional.h>
560
561#include <bsls_assert.h>
562#include <bsls_platform.h>
563
564#include <cstddef>
565
566#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
567#include <bsls_nativestd.h>
568#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
569
570
571namespace bslalg {
572
573 // =======================================
574 // class HashTableImpUtil_ExtractKeyResult
575 // =======================================
576
577template <class KEY_CONFIG>
579
580 typedef typename KEY_CONFIG::KeyType KeyType;
581 typedef typename KEY_CONFIG::ValueType ValueType;
582
583 struct ConstMatch { char dummy[ 1]; };
584 struct NonConstMatch { char dummy[17]; };
585 struct ConversionMatch { char dummy[65]; };
586
587 struct Impl {
588 template <class ARG>
589 static ConstMatch test(const KeyType& (*)(const ARG &));
590
591 template<class ARG>
592 static NonConstMatch test(KeyType& (*)(ARG &));
593
594 template<class RESULT, class ARG>
595 static ConversionMatch test(RESULT (*)(ARG));
596 };
597
598 enum { RESULT_SELECTOR = sizeof(Impl::test(&KEY_CONFIG::extractKey)) };
599
600 typedef typename bsl::conditional<RESULT_SELECTOR == sizeof(ConstMatch),
601 const KeyType&,
602 typename bsl::conditional<RESULT_SELECTOR == sizeof(NonConstMatch),
603 KeyType&,
604 KeyType>::type>::type Type;
605};
606
607 // ======================
608 // class HashTableImpUtil
609 // ======================
610
611/// This `struct` provides a namespace for a suite of utility functions
612/// for creating and manipulating a hash table.
614
615 private:
616 // PRIVATE TYPES
617 typedef std::size_t size_t;
618
619 // PRIVATE CLASS METHODS
620
621 /// Return the address of the `HashTableBucket` in the array of buckets
622 /// referred to by the specified hash-table `anchor` whose index is the
623 /// adjusted value of the specified `hashCode` (see
624 /// `computeBucketIndex`). The behavior is undefined if `anchor`
625 /// has 0 buckets.
626 static HashTableBucket *findBucketForHashCode(
627 const HashTableAnchor& anchor,
628 std::size_t hashCode);
629
630 public:
631 // CLASS METHODS
632
633 /// Return `true` if the specified `linkAddress` is the address of one
634 /// of the links in the list of elements in the closed range
635 /// `[bucket.first(), bucket.last()]`.
636 static bool bucketContainsLink(const HashTableBucket& bucket,
637 BidirectionalLink *linkAddress);
638
639 /// Return a reference providing non-modifiable access to the
640 /// key (of type `KEY_CONFIG::KeyType`) held by the specified
641 /// `link`. The behavior is undefined unless `link` refers to a node
642 /// of type `BidirectionalNode<KEY_CONFIG::ValueType>`. `KEY_CONFIG`
643 /// shall be a namespace providing the type names `KeyType` and
644 /// `ValueType`, as well as a function that can be called as if it had
645 /// the following signature:
646 /// @code
647 /// const KeyType& extractKey(const ValueType& obj);
648 /// @endcode
649 template<class KEY_CONFIG>
652
653 /// Return a reference providing non-modifiable access to the
654 /// value (of type `KEY_CONFIG::ValueType`) held by the specified
655 /// `link`. The behavior is undefined unless `link` refers to a node
656 /// of type `BidirectionalNode<KEY_CONFIG::ValueType>`. `KEY_CONFIG`
657 /// shall be a namespace providing the type name `ValueType`.
658 template <class KEY_CONFIG>
659 static typename KEY_CONFIG::ValueType& extractValue(
660 BidirectionalLink *link);
661
662 /// Return `true` if the specified `anchor` is well-formed for the
663 /// specified `hasher`. Use the specified `allocator` for temporary
664 /// memory, or the default allocator if none is specified. For a
665 /// `HashTableAnchor` to be considered well-formed for a particular key
666 /// policy, `KEY_CONFIG`, and hash functor, `hasher`, all of the
667 /// following must be true:
668 ///
669 /// 1. The `anchor.listRootAddress()` is the address of a
670 /// well-formed doubly linked list (see
671 /// @ref bslalg_bidirectionallinklistutil ).
672 /// 2. Links in the doubly linked list having the same adjusted hash
673 /// value are contiguous, where the adjusted hash value is the value
674 /// returned by `computeBucketIndex`, for
675 /// `extractKey<KEY_CONFIG>(link)` and `anchor.bucketArraySize()`.
676 /// 3. Links in the doubly linked list having the same hash value are
677 /// contiguous.
678 /// 4. The first and last links in each bucket (in the bucket array,
679 /// anchor.bucketArrayAddress()') refer to a the first and last
680 /// element in the well-formed doubly linked list of all nodes in the
681 /// list having an adjusted hash value equal to that bucket's array
682 /// index. If no values in the doubly linked list have an adjusted
683 /// hash value equal to a bucket's index, then the addresses of the
684 /// first and last links for that bucket are 0.
685 template <class KEY_CONFIG, class HASHER>
686 static bool isWellFormed(const HashTableAnchor& anchor,
687 const HASHER& hasher,
688 bslma::Allocator *allocator = 0);
689
690 /// Return the index of the bucket referring to the elements whose
691 /// adjusted hash codes are the same as the adjusted value of the
692 /// specified `hashCode`, where `hashCode` (and the hash-codes of the
693 /// elements) are adjusted for the specified `numBuckets`. The behavior
694 /// is undefined if `numBuckets` is 0.
695 static std::size_t computeBucketIndex(std::size_t hashCode,
696 std::size_t numBuckets);
697
698 /// Insert the specified `link`, having the specified (non-adjusted)
699 /// `hashCode`, into the specified `anchor`, at the front of the
700 /// bucket with index
701 /// `computeBucketIndex(hashCode, anchor->bucketArraySize())`. The
702 /// behavior is undefined unless `anchor` is well-formed (see
703 /// `isWellFormed`) for some combination of `KEY_CONFIG` and `HASHER`
704 /// such that `link` refers to a node of type
705 /// `BidirectionalNode<KEY_CONFIG::ValueType>` and
706 /// `HASHER(extractKey<KEY_CONFIG>(link))` returns `hashCode`.
708 BidirectionalLink *link,
709 std::size_t hashCode);
710
711 /// Insert the specified `link`, having the specified (non-adjusted)
712 /// `hashCode`, into the specified `anchor`, into the bucket with index
713 /// `computeBucketIndex(hashCode, anchor->bucketArraySize())`, after the
714 /// last node in the bucket. The behavior is undefined unless `anchor`
715 /// is well-formed (see `isWellFormed`) for some combination of
716 /// `KEY_CONFIG` and `HASHER` such that `link` refers to a node of type
717 /// `BidirectionalNode<KEY_CONFIG::ValueType>` and
718 /// `HASHER(extractKey<KEY_CONFIG>(link))` returns `hashCode`.
720 BidirectionalLink *link,
721 std::size_t hashCode);
722
723 /// Insert the specified `link`, having the specified (non-adjusted)
724 /// `hashCode`, into the specified `anchor` immediately before the
725 /// specified `position` in the bi-directional linked list of `anchor`.
726 /// The behavior is undefined unless position is in the bucket having
727 /// index `computeBucketIndex(hashCode, anchor->bucketArraySize())` and
728 /// `anchor` is well-formed (see `isWellFormed`) for some combination of
729 /// `KEY_CONFIG` and `HASHER` such that `link` refers to a node of type
730 /// `BidirectionalNode<KEY_CONFIG::ValueType>` and
731 /// `HASHER(extractKey<KEY_CONFIG>(link))` returns `hashCode`.
733 BidirectionalLink *link,
734 std::size_t hashCode,
735 BidirectionalLink *position);
736
737 /// Remove the specified `link`, having the specified (non-adjusted)
738 /// `hashCode`, from the specified `anchor`. The behavior is undefined
739 /// unless `anchor` is well-formed (see `isWellFormed`) for some
740 /// combination of `KEY_CONFIG` and `HASHER` such that `link` refers to
741 /// a node of type `BidirectionalNode<KEY_CONFIG::ValueType>` and
742 /// `HASHER(extractKey<KEY_CONFIG>(link))` returns `hashCode`.
743 static void remove(HashTableAnchor *anchor,
744 BidirectionalLink *link,
745 std::size_t hashCode);
746
747 /// Return the address of the first link in the list element of
748 /// the specified `anchor`, having a value matching (according to the
749 /// specified `equalityFunctor`) the specified `key` in the bucket that
750 /// holds elements with the specified `hashCode` if such a link exists,
751 /// and return 0 otherwise. The behavior is undefined unless, for the
752 /// provided `KEY_CONFIG` and some hash function, `HASHER`, `anchor` is
753 /// well-formed (see `isWellFormed`) and `HASHER(key)` returns
754 /// `hashCode`. `KEY_CONFIG` shall be a
755 /// namespace providing the type names `KeyType` and `ValueType`, as
756 /// well as a function that can be called as if it had the following
757 /// signature:
758 /// @code
759 /// const KeyType& extractKey(const ValueType& obj);
760 /// @endcode
761 /// `KEY_EQUAL` shall be a functor that can be called as if it had the
762 /// following signature:
763 /// @code
764 /// bool operator()(const KEY_CONFIG::KeyType& key1,
765 /// const KEY_CONFIG::KeyType& key2)
766 /// @endcode
767 template <class KEY_CONFIG, class KEY_EQUAL>
768 static BidirectionalLink *find(
769 const HashTableAnchor& anchor,
771 const KEY_EQUAL& equalityFunctor,
772 std::size_t hashCode);
773
774 /// Return the address of the first link in the list element of the
775 /// specified `anchor` having a value matching (according to the
776 /// specified transparent `equalityFunctor`) the specified `key` in the
777 /// bucket that holds elements with the specified `hashCode` if such a
778 /// link exists, and return 0 otherwise. The behavior is undefined
779 /// unless, for the provided `KEY_CONFIG` and some hash function,
780 /// `HASHER`, `anchor` is well-formed (see `isWellFormed`) and
781 /// `HASHER(key)` returns `hashCode`. `KEY_CONFIG` shall be a
782 /// namespace providing the type names `KeyType` and `ValueType`, as
783 /// well as a function that can be called as if it had the following
784 /// signature:
785 /// @code
786 /// const KeyType& extractKey(const ValueType& obj);
787 /// @endcode
788 /// `KEY_EQUAL` shall be a functor that can be called as if it had the
789 /// following signature:
790 /// @code
791 /// bool operator()(const LOOKUP_KEY& key1,
792 /// const KEY_CONFIG::KeyType& key2)
793 ///
794 /// @endcode
795 template <class KEY_CONFIG, class LOOKUP_KEY, class KEY_EQUAL>
797 const HashTableAnchor& anchor,
798 const LOOKUP_KEY& key,
799 const KEY_EQUAL& equalityFunctor,
800 std::size_t hashCode);
801
802 /// Populate the specified `newHashTable` with all the elements in the
803 /// specified `elementList`, using the specified `hasher` to determine
804 /// the (non-adjusted) hash code for each element. This operation
805 /// provides the strong exception guarantee unless the supplied `hasher`
806 /// throws, in which case it provides no exception safety guarantee.
807 /// The buckets in the array in `newAnchor` and the list root address in
808 /// `newAnchor` are assumed to be garbage and overwritten. The behavior
809 /// is undefined unless, `newHashTable` holds no elements and has one or
810 /// more (empty) buckets, and `elementList` is a well-formed
811 /// bi-directional list (see `BidirectionalLinkListUtil::isWellFormed`)
812 /// whose nodes are each of type
813 /// `BidirectionalNode<KEY_CONFIG::ValueType>`, the previous address of
814 /// the first node and the next address of the last node are 0.
815 template <class KEY_CONFIG, class HASHER>
816 static void rehash(HashTableAnchor *newAnchor,
817 BidirectionalLink *elementList,
818 const HASHER& hasher);
819};
820
821// ============================================================================
822// TEMPLATE AND INLINE FUNCTION DEFINITIONS
823// ============================================================================
824
825 //-----------------------
826 // class HashTableImpUtil
827 //-----------------------
828
829// PRIVATE CLASS METHODS
830inline
831HashTableBucket *HashTableImpUtil::findBucketForHashCode(
832 const HashTableAnchor& anchor,
833 std::size_t hashCode)
834{
837
838 std::size_t bucketId = HashTableImpUtil::computeBucketIndex(
839 hashCode,
840 anchor.bucketArraySize());
841 return &(anchor.bucketArrayAddress()[bucketId]);
842}
843
844inline
845std::size_t HashTableImpUtil::computeBucketIndex(std::size_t hashCode,
846 std::size_t numBuckets)
847{
848 BSLS_ASSERT_SAFE(0 != numBuckets);
849
850 return hashCode % numBuckets;
851}
852
853/// Return true the specified `link` is contained in the specified `bucket`
854/// and false otherwise.
855inline
857 BidirectionalLink *linkAddress)
858{
859 BSLS_ASSERT_SAFE(!bucket.first() == !bucket.last());
860
861 for (BidirectionalLink *cursor = bucket.first(),
862 * const end = bucket.end(); end != cursor;
863 cursor = cursor->nextLink()) {
864 if (linkAddress == cursor) {
865 return true; // RETURN
866 }
867
868 BSLS_ASSERT_SAFE(cursor);
869 }
870
871 return false;
872}
873
874// CLASS METHODS
875template<class KEY_CONFIG>
876inline
877typename KEY_CONFIG::ValueType& HashTableImpUtil::extractValue(
878 BidirectionalLink *link)
879{
880 BSLS_ASSERT_SAFE(link);
881
883 return static_cast<BNode *>(link)->value();
884}
885
886template<class KEY_CONFIG>
887inline
890{
891 BSLS_ASSERT_SAFE(link);
892
894
895 BNode *node = static_cast<BNode *>(link);
896 return KEY_CONFIG::extractKey(node->value());
897}
898
899template <class KEY_CONFIG, class KEY_EQUAL>
900inline
902 const HashTableAnchor& anchor,
904 const KEY_EQUAL& equalityFunctor,
905 std::size_t hashCode)
906{
909
910 const HashTableBucket *bucket = findBucketForHashCode(anchor, hashCode);
911 BSLS_ASSERT_SAFE(bucket);
912
913 for (BidirectionalLink *cursor = bucket->first(),
914 * const end = bucket->end();
915 end != cursor; cursor = cursor->nextLink() ) {
916 if (equalityFunctor(key, extractKey<KEY_CONFIG>(cursor))) {
917 return cursor; // RETURN
918 }
919 }
920
921 return 0;
922}
923
924template <class KEY_CONFIG, class LOOKUP_KEY, class KEY_EQUAL>
925inline
927 const HashTableAnchor& anchor,
928 const LOOKUP_KEY& key,
929 const KEY_EQUAL& equalityFunctor,
930 std::size_t hashCode)
931{
934
935 const HashTableBucket *bucket = findBucketForHashCode(anchor, hashCode);
936 BSLS_ASSERT_SAFE(bucket);
937
938 for (BidirectionalLink *cursor = bucket->first(),
939 * const end = bucket->end();
940 end != cursor; cursor = cursor->nextLink() ) {
941 if (equalityFunctor(key, extractKey<KEY_CONFIG>(cursor))) {
942 return cursor; // RETURN
943 }
944 }
945
946 return 0;
947}
948
949template <class KEY_CONFIG, class HASHER>
951 BidirectionalLink *elementList,
952 const HASHER& hasher)
953{
954 BSLS_ASSERT_SAFE(newAnchor);
956 BSLS_ASSERT_SAFE(0 != newAnchor->bucketArraySize());
957 BSLS_ASSERT_SAFE(!elementList || !elementList->previousLink());
958
959 /// An object of this proctor class guarantees that, on leaving scope,
960 /// any remaining elements in the original specified `elementList` are
961 /// spliced to the front of the list rooted in the specified `newAnchor`
962 /// so that there is only one list for the client to clear if an
963 /// exception is thrown by a user supplied hash functor. Note that it
964 /// might be possible to avoid creating such a proctor in C++11 if the
965 /// hash functor is determined to be `noexcept`.
966 ///
967 /// See @ref bslalg_hashtableimputil
968 class Proctor {
969
970 private:
971 BidirectionalLink **d_sourceList;
972 HashTableAnchor *d_targetAnchor;
973
974#if !defined(BSLS_PLATFORM_CMP_MSVC) // Microsoft warns if these
975 Proctor(const Proctor&); // = delete; // methods are declared private.
976 Proctor& operator=(const Proctor&); // = delete;
977#endif
978
979 public:
980 Proctor(BidirectionalLink **sourceList,
981 HashTableAnchor *targetAnchor)
982 : d_sourceList(sourceList)
983 , d_targetAnchor(targetAnchor)
984 {
985 BSLS_ASSERT(sourceList);
986 BSLS_ASSERT(targetAnchor);
987 }
988
989 ~Proctor()
990 {
991 if (BidirectionalLink *lastLink = *d_sourceList) {
992 for( ; lastLink->nextLink(); lastLink = lastLink->nextLink()) {
993 // This loop body is intentionally left blank.
994 }
995 BidirectionalLinkListUtil::spliceListBeforeTarget(
996 *d_sourceList,
997 lastLink,
998 d_targetAnchor->listRootAddress());
999 }
1000 }
1001 };
1002
1003 // The callers of this function should be rewritten to take into account
1004 // that it is the responsibility of this function, not its callers, to zero
1005 // out the buckets.
1006
1007 for (void **cursor = (void **) newAnchor->bucketArrayAddress(),
1008 ** const end = (void **) (newAnchor->bucketArrayAddress() +
1009 newAnchor->bucketArraySize());
1010 cursor < end; ++cursor) {
1011 *cursor = 0;
1012 }
1013 newAnchor->setListRootAddress(0);
1014
1015 Proctor enforceSingleListOnExit(&elementList, newAnchor);
1016
1017 while (elementList) {
1018 BidirectionalLink *nextNode = elementList;
1019 elementList = elementList->nextLink();
1020
1021 insertAtBackOfBucket(newAnchor,
1022 nextNode,
1023 hasher(extractKey<KEY_CONFIG>(nextNode)));
1024 }
1025}
1026
1027template <class KEY_CONFIG, class HASHER>
1029 const HASHER& hasher,
1030 bslma::Allocator *allocator)
1031{
1032 HashTableBucket *array = anchor.bucketArrayAddress();
1033 size_t size = anchor.bucketArraySize();
1034 BidirectionalLink *root = anchor.listRootAddress();
1035
1036 if (!array || !size) {
1037 return false; // RETURN
1038 }
1039
1040 if (!root) {
1041 // An empty list, so there should be no pointers set in the bucket
1042 // array.
1043 for (size_t i = 0; i < size; ++i) {
1044 const HashTableBucket& b = array[i];
1045 if (b.first() || b.last()) {
1046 return false; // RETURN
1047 }
1048 }
1049
1050 return true; // RETURN
1051 }
1052
1053 if (!allocator) {
1055 }
1056
1057 bool *bucketsUsed = (bool *) allocator->allocate(size);
1058 bslma::DeallocatorGuard<bslma::Allocator> guard(bucketsUsed, allocator);
1059 for (size_t i = 0; i < size; ++i) {
1060 bucketsUsed[i] = false;
1061 }
1062
1063 size_t hash = hasher(extractKey<KEY_CONFIG>(root));
1064 size_t bucketIdx = computeBucketIndex(hash, size);
1065 if (array[bucketIdx].first() != root) {
1066 return false; // RETURN
1067 }
1068
1069 bucketsUsed[bucketIdx] = true;
1070
1071 BidirectionalLink *prev = root;
1072 size_t prevBucketIdx = bucketIdx;
1073 while (BidirectionalLink *cursor = prev->nextLink()) {
1074 if (cursor->previousLink() != prev) {
1075 return false; // RETURN
1076 }
1077
1078 hash = hasher(extractKey<KEY_CONFIG>(cursor));
1079 bucketIdx = computeBucketIndex(hash, size);
1080
1081 if (bucketIdx != prevBucketIdx) {
1082 // New bucket
1083
1084 // We should be the first node in the new bucket, so if this
1085 // bucket's been visited before, it's an error.
1086
1087 if (bucketsUsed[bucketIdx]) {
1088 return false; // RETURN
1089 }
1090 bucketsUsed[bucketIdx] = true;
1091
1092 // Since we're the first node in the bucket, bucket.first()
1093 // should point at us.
1094
1095 if (array[bucketIdx].first() != cursor) {
1096 return false; // RETURN
1097 }
1098
1099 // 'last()' of the previous bucket should point at the
1100 // previous node.
1101
1102 if (array[prevBucketIdx].last() != prev) {
1103 return false; // RETURN
1104 }
1105 }
1106
1107 // Set 'prev' variables for next iteration
1108 prev = cursor;
1109 prevBucketIdx = bucketIdx;
1110 }
1111
1112 if (array[prevBucketIdx].last() != prev) {
1113 return false; // RETURN
1114 }
1115
1116 // Check that traversing the root list traversed all non-empty buckets.
1117
1118 for (size_t i = 0; i < size; ++i) {
1119 const HashTableBucket& b = array[i];
1120 if (bucketsUsed[i]) {
1121 if (!b.first() || !b.last()) {
1122 return false; // RETURN
1123 }
1124 }
1125 else if ( b.first() || b.last()) {
1126 return false; // RETURN
1127 }
1128 }
1129
1130 return true;
1131}
1132
1133} // close package namespace
1134
1135
1136#endif
1137
1138// ----------------------------------------------------------------------------
1139// Copyright 2013 Bloomberg Finance L.P.
1140//
1141// Licensed under the Apache License, Version 2.0 (the "License");
1142// you may not use this file except in compliance with the License.
1143// You may obtain a copy of the License at
1144//
1145// http://www.apache.org/licenses/LICENSE-2.0
1146//
1147// Unless required by applicable law or agreed to in writing, software
1148// distributed under the License is distributed on an "AS IS" BASIS,
1149// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1150// See the License for the specific language governing permissions and
1151// limitations under the License.
1152// ----------------------------- END-OF-FILE ----------------------------------
1153
1154/** @} */
1155/** @} */
1156/** @} */
Definition bslalg_bidirectionalnode.h:357
Definition bslalg_hashtableanchor.h:541
BidirectionalLink * listRootAddress() const
Return the value listRootAddress attribute of this object.
Definition bslalg_hashtableanchor.h:708
std::size_t bucketArraySize() const
Return the value of the bucketArraySize attribute of this object.
Definition bslalg_hashtableanchor.h:714
void setListRootAddress(BidirectionalLink *value)
Definition bslalg_hashtableanchor.h:691
HashTableBucket * bucketArrayAddress() const
Definition bslalg_hashtableanchor.h:720
Definition bslma_allocator.h:457
virtual void * allocate(size_type size)=0
Definition bslma_deallocatorguard.h:166
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1804
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1762
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlc_flathashmap.h:1805
Definition bslmf_conditional.h:120
Definition bslmf_integralconstant.h:244
Definition bslalg_hashtablebucket.h:297
BidirectionalLink * first() const
Definition bslalg_hashtablebucket.h:418
BidirectionalLink * end() const
Definition bslalg_hashtablebucket.h:412
BidirectionalLink * last() const
Definition bslalg_hashtablebucket.h:424
Definition bslalg_hashtableimputil.h:583
char dummy[1]
Definition bslalg_hashtableimputil.h:583
Definition bslalg_hashtableimputil.h:585
char dummy[65]
Definition bslalg_hashtableimputil.h:585
Definition bslalg_hashtableimputil.h:587
static ConversionMatch test(RESULT(*)(ARG))
static NonConstMatch test(KeyType &(*)(ARG &))
static ConstMatch test(const KeyType &(*)(const ARG &))
Definition bslalg_hashtableimputil.h:584
char dummy[17]
Definition bslalg_hashtableimputil.h:584
Definition bslalg_hashtableimputil.h:578
KEY_CONFIG::KeyType KeyType
Definition bslalg_hashtableimputil.h:580
bsl::conditional< RESULT_SELECTOR==sizeof(ConstMatch), constKeyType &, typenamebsl::conditional< RESULT_SELECTOR==sizeof(NonConstMatch), KeyType &, KeyType >::type >::type Type
Definition bslalg_hashtableimputil.h:604
@ RESULT_SELECTOR
Definition bslalg_hashtableimputil.h:598
KEY_CONFIG::ValueType ValueType
Definition bslalg_hashtableimputil.h:581
Definition bslalg_hashtableimputil.h:613
static HashTableImpUtil_ExtractKeyResult< KEY_CONFIG >::Type extractKey(BidirectionalLink *link)
Definition bslalg_hashtableimputil.h:889
static BidirectionalLink * findTransparent(const HashTableAnchor &anchor, const LOOKUP_KEY &key, const KEY_EQUAL &equalityFunctor, std::size_t hashCode)
Definition bslalg_hashtableimputil.h:926
static KEY_CONFIG::ValueType & extractValue(BidirectionalLink *link)
Definition bslalg_hashtableimputil.h:877
static bool isWellFormed(const HashTableAnchor &anchor, const HASHER &hasher, bslma::Allocator *allocator=0)
Definition bslalg_hashtableimputil.h:1028
static bool bucketContainsLink(const HashTableBucket &bucket, BidirectionalLink *linkAddress)
Definition bslalg_hashtableimputil.h:856
static void rehash(HashTableAnchor *newAnchor, BidirectionalLink *elementList, const HASHER &hasher)
Definition bslalg_hashtableimputil.h:950
static void insertAtFrontOfBucket(HashTableAnchor *anchor, BidirectionalLink *link, std::size_t hashCode)
static void remove(HashTableAnchor *anchor, BidirectionalLink *link, std::size_t hashCode)
static void insertAtPosition(HashTableAnchor *anchor, BidirectionalLink *link, std::size_t hashCode, BidirectionalLink *position)
static void insertAtBackOfBucket(HashTableAnchor *anchor, BidirectionalLink *link, std::size_t hashCode)
static std::size_t computeBucketIndex(std::size_t hashCode, std::size_t numBuckets)
Definition bslalg_hashtableimputil.h:845
static BidirectionalLink * find(const HashTableAnchor &anchor, typename HashTableImpUtil_ExtractKeyResult< KEY_CONFIG >::Type key, const KEY_EQUAL &equalityFunctor, std::size_t hashCode)
Definition bslalg_hashtableimputil.h:901
static Allocator * defaultAllocator()
Definition bslma_default.h:889