BDE 4.39.x 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.
613///
614/// See @ref bslalg_hashtableimputil
616
617 private:
618 // PRIVATE TYPES
619 typedef std::size_t size_t;
620
621 // PRIVATE CLASS METHODS
622
623 /// Return the address of the `HashTableBucket` in the array of buckets
624 /// referred to by the specified hash-table `anchor` whose index is the
625 /// adjusted value of the specified `hashCode` (see `computeBucketIndex`).
626 ///
627 /// \pre The behavior is undefined if `anchor`
628 /// has 0 buckets.
629 static HashTableBucket *findBucketForHashCode(
630 const HashTableAnchor& anchor,
631 std::size_t hashCode);
632
633 public:
634 // CLASS METHODS
635
636 /// Return `true` if the specified `linkAddress` is the address of one
637 /// of the links in the list of elements in the closed range
638 /// `[bucket.first(), bucket.last()]`.
639 static bool bucketContainsLink(const HashTableBucket& bucket,
640 BidirectionalLink *linkAddress);
641
642 /// Return a reference providing non-modifiable access to the
643 /// key (of type `KEY_CONFIG::KeyType`) held by the specified `link`.
644 ///
645 /// \pre The behavior is undefined unless `link` refers to a node
646 /// of type `BidirectionalNode<KEY_CONFIG::ValueType>`. `KEY_CONFIG`
647 /// shall be a namespace providing the type names `KeyType` and
648 /// `ValueType`, as well as a function that can be called as if it had
649 /// the following signature:
650 /// @code
651 /// const KeyType& extractKey(const ValueType& obj);
652 /// @endcode
653 template<class KEY_CONFIG>
656
657 /// Return a reference providing non-modifiable access to the
658 /// value (of type `KEY_CONFIG::ValueType`) held by the specified `link`.
659 ///
660 /// \pre The behavior is undefined unless `link` refers to a node
661 /// of type `BidirectionalNode<KEY_CONFIG::ValueType>`. `KEY_CONFIG`
662 /// shall be a namespace providing the type name `ValueType`.
663 template <class KEY_CONFIG>
664 static typename KEY_CONFIG::ValueType& extractValue(
665 BidirectionalLink *link);
666
667 /// Return `true` if the specified `anchor` is well-formed for the
668 /// specified `hasher`. Use the specified `allocator` for temporary
669 /// memory, or the default allocator if none is specified. For a
670 /// `HashTableAnchor` to be considered well-formed for a particular key
671 /// policy, `KEY_CONFIG`, and hash functor, `hasher`, all of the
672 /// following must be true:
673 ///
674 /// 1. The `anchor.listRootAddress()` is the address of a
675 /// well-formed doubly linked list (see
676 /// @ref bslalg_bidirectionallinklistutil ).
677 /// 2. Links in the doubly linked list having the same adjusted hash
678 /// value are contiguous, where the adjusted hash value is the value
679 /// returned by `computeBucketIndex`, for
680 /// `extractKey<KEY_CONFIG>(link)` and `anchor.bucketArraySize()`.
681 /// 3. Links in the doubly linked list having the same hash value are
682 /// contiguous.
683 /// 4. The first and last links in each bucket (in the bucket array,
684 /// anchor.bucketArrayAddress()') refer to a the first and last
685 /// element in the well-formed doubly linked list of all nodes in the
686 /// list having an adjusted hash value equal to that bucket's array
687 /// index. If no values in the doubly linked list have an adjusted
688 /// hash value equal to a bucket's index, then the addresses of the
689 /// first and last links for that bucket are 0.
690 template <class KEY_CONFIG, class HASHER>
691 static bool isWellFormed(const HashTableAnchor& anchor,
692 const HASHER& hasher,
693 bslma::Allocator *allocator = 0);
694
695 /// Return the index of the bucket referring to the elements whose
696 /// adjusted hash codes are the same as the adjusted value of the
697 /// specified `hashCode`, where `hashCode` (and the hash-codes of the
698 /// elements) are adjusted for the specified `numBuckets`.
699 ///
700 /// \pre The behavior is undefined if `numBuckets` is 0.
701 static std::size_t computeBucketIndex(std::size_t hashCode,
702 std::size_t numBuckets);
703
704 /// Insert the specified `link`, having the specified (non-adjusted)
705 /// `hashCode`, into the specified `anchor`, at the front of the
706 /// bucket with index
707 /// `computeBucketIndex(hashCode, anchor->bucketArraySize())`.
708 ///
709 /// \pre The behavior is undefined unless `anchor` is well-formed (see
710 /// `isWellFormed`) for some combination of `KEY_CONFIG` and `HASHER`
711 /// such that `link` refers to a node of type
712 /// `BidirectionalNode<KEY_CONFIG::ValueType>` and
713 /// `HASHER(extractKey<KEY_CONFIG>(link))` returns `hashCode`.
715 BidirectionalLink *link,
716 std::size_t hashCode);
717
718 /// Insert the specified `link`, having the specified (non-adjusted)
719 /// `hashCode`, into the specified `anchor`, into the bucket with index
720 /// `computeBucketIndex(hashCode, anchor->bucketArraySize())`, after the last node in the bucket.
721 ///
722 /// \pre The behavior is undefined unless `anchor`
723 /// is well-formed (see `isWellFormed`) for some combination of
724 /// `KEY_CONFIG` and `HASHER` such that `link` refers to a node of type
725 /// `BidirectionalNode<KEY_CONFIG::ValueType>` and
726 /// `HASHER(extractKey<KEY_CONFIG>(link))` returns `hashCode`.
728 BidirectionalLink *link,
729 std::size_t hashCode);
730
731 /// Insert the specified `link`, having the specified (non-adjusted)
732 /// `hashCode`, into the specified `anchor` immediately before the
733 /// specified `position` in the bi-directional linked list of `anchor`.
734 ///
735 /// \pre The behavior is undefined unless position is in the bucket having
736 /// index `computeBucketIndex(hashCode, anchor->bucketArraySize())` and
737 /// `anchor` is well-formed (see `isWellFormed`) for some combination of
738 /// `KEY_CONFIG` and `HASHER` such that `link` refers to a node of type
739 /// `BidirectionalNode<KEY_CONFIG::ValueType>` and
740 /// `HASHER(extractKey<KEY_CONFIG>(link))` returns `hashCode`.
742 BidirectionalLink *link,
743 std::size_t hashCode,
744 BidirectionalLink *position);
745
746 /// Remove the specified `link`, having the specified (non-adjusted)
747 /// `hashCode`, from the specified `anchor`.
748 ///
749 /// \pre The behavior is undefined unless `anchor` is well-formed (see `isWellFormed`) for some
750 /// combination of `KEY_CONFIG` and `HASHER` such that `link` refers to
751 /// a node of type `BidirectionalNode<KEY_CONFIG::ValueType>` and
752 /// `HASHER(extractKey<KEY_CONFIG>(link))` returns `hashCode`.
753 static void remove(HashTableAnchor *anchor,
754 BidirectionalLink *link,
755 std::size_t hashCode);
756
757 /// Return the address of the first link in the list element of
758 /// the specified `anchor`, having a value matching (according to the
759 /// specified `equalityFunctor`) the specified `key` in the bucket that
760 /// holds elements with the specified `hashCode` if such a link exists, and return 0 otherwise.
761 ///
762 /// \pre The behavior is undefined unless, for the
763 /// provided `KEY_CONFIG` and some hash function, `HASHER`, `anchor` is
764 /// well-formed (see `isWellFormed`) and `HASHER(key)` returns
765 /// `hashCode`. `KEY_CONFIG` shall be a
766 /// namespace providing the type names `KeyType` and `ValueType`, as
767 /// well as a function that can be called as if it had the following
768 /// signature:
769 /// @code
770 /// const KeyType& extractKey(const ValueType& obj);
771 /// @endcode
772 /// `KEY_EQUAL` shall be a functor that can be called as if it had the
773 /// following signature:
774 /// @code
775 /// bool operator()(const KEY_CONFIG::KeyType& key1,
776 /// const KEY_CONFIG::KeyType& key2)
777 /// @endcode
778 template <class KEY_CONFIG, class KEY_EQUAL>
779 static BidirectionalLink *find(
780 const HashTableAnchor& anchor,
782 const KEY_EQUAL& equalityFunctor,
783 std::size_t hashCode);
784
785 /// Return the address of the first link in the list element of the
786 /// specified `anchor` having a value matching (according to the
787 /// specified transparent `equalityFunctor`) the specified `key` in the
788 /// bucket that holds elements with the specified `hashCode` if such a
789 /// link exists, and return 0 otherwise.
790 ///
791 /// \pre The behavior is undefined unless, for the provided `KEY_CONFIG` and some hash function,
792 /// `HASHER`, `anchor` is well-formed (see `isWellFormed`) and
793 /// `HASHER(key)` returns `hashCode`. `KEY_CONFIG` shall be a
794 /// namespace providing the type names `KeyType` and `ValueType`, as
795 /// well as a function that can be called as if it had the following
796 /// signature:
797 /// @code
798 /// const KeyType& extractKey(const ValueType& obj);
799 /// @endcode
800 /// `KEY_EQUAL` shall be a functor that can be called as if it had the
801 /// following signature:
802 /// @code
803 /// bool operator()(const LOOKUP_KEY& key1,
804 /// const KEY_CONFIG::KeyType& key2)
805 ///
806 /// @endcode
807 template <class KEY_CONFIG, class LOOKUP_KEY, class KEY_EQUAL>
809 const HashTableAnchor& anchor,
810 const LOOKUP_KEY& key,
811 const KEY_EQUAL& equalityFunctor,
812 std::size_t hashCode);
813
814 /// Populate the specified `newHashTable` with all the elements in the
815 /// specified `elementList`, using the specified `hasher` to determine
816 /// the (non-adjusted) hash code for each element. This operation
817 /// provides the strong exception guarantee unless the supplied `hasher`
818 /// throws, in which case it provides no exception safety guarantee.
819 /// The buckets in the array in `newAnchor` and the list root address in
820 /// `newAnchor` are assumed to be garbage and overwritten.
821 ///
822 /// \pre The behavior is undefined unless, `newHashTable` holds no elements and has one or
823 /// more (empty) buckets, and `elementList` is a well-formed
824 /// bi-directional list (see `BidirectionalLinkListUtil::isWellFormed`)
825 /// whose nodes are each of type
826 /// `BidirectionalNode<KEY_CONFIG::ValueType>`, the previous address of
827 /// the first node and the next address of the last node are 0.
828 template <class KEY_CONFIG, class HASHER>
829 static void rehash(HashTableAnchor *newAnchor,
830 BidirectionalLink *elementList,
831 const HASHER& hasher);
832};
833
834// ============================================================================
835// TEMPLATE AND INLINE FUNCTION DEFINITIONS
836// ============================================================================
837
838 //-----------------------
839 // class HashTableImpUtil
840 //-----------------------
841
842// PRIVATE CLASS METHODS
843inline
844HashTableBucket *HashTableImpUtil::findBucketForHashCode(
845 const HashTableAnchor& anchor,
846 std::size_t hashCode)
847{
850
851 std::size_t bucketId = HashTableImpUtil::computeBucketIndex(
852 hashCode,
853 anchor.bucketArraySize());
854 return &(anchor.bucketArrayAddress()[bucketId]);
855}
856
857inline
858std::size_t HashTableImpUtil::computeBucketIndex(std::size_t hashCode,
859 std::size_t numBuckets)
860{
861 BSLS_ASSERT_SAFE(0 != numBuckets);
862
863 return hashCode % numBuckets;
864}
865
866/// Return true the specified `link` is contained in the specified `bucket`
867/// and false otherwise.
868inline
870 BidirectionalLink *linkAddress)
871{
872 BSLS_ASSERT_SAFE(!bucket.first() == !bucket.last());
873
874 for (BidirectionalLink *cursor = bucket.first(),
875 * const end = bucket.end(); end != cursor;
876 cursor = cursor->nextLink()) {
877 if (linkAddress == cursor) {
878 return true; // RETURN
879 }
880
881 BSLS_ASSERT_SAFE(cursor);
882 }
883
884 return false;
885}
886
887// CLASS METHODS
888template<class KEY_CONFIG>
889inline
890typename KEY_CONFIG::ValueType& HashTableImpUtil::extractValue(
891 BidirectionalLink *link)
892{
893 BSLS_ASSERT_SAFE(link);
894
896 return static_cast<BNode *>(link)->value();
897}
898
899template<class KEY_CONFIG>
900inline
903{
904 BSLS_ASSERT_SAFE(link);
905
907
908 BNode *node = static_cast<BNode *>(link);
909 return KEY_CONFIG::extractKey(node->value());
910}
911
912template <class KEY_CONFIG, class KEY_EQUAL>
913inline
915 const HashTableAnchor& anchor,
917 const KEY_EQUAL& equalityFunctor,
918 std::size_t hashCode)
919{
922
923 const HashTableBucket *bucket = findBucketForHashCode(anchor, hashCode);
924 BSLS_ASSERT_SAFE(bucket);
925
926 for (BidirectionalLink *cursor = bucket->first(),
927 * const end = bucket->end();
928 end != cursor; cursor = cursor->nextLink() ) {
929 if (equalityFunctor(key, extractKey<KEY_CONFIG>(cursor))) {
930 return cursor; // RETURN
931 }
932 }
933
934 return 0;
935}
936
937template <class KEY_CONFIG, class LOOKUP_KEY, class KEY_EQUAL>
938inline
940 const HashTableAnchor& anchor,
941 const LOOKUP_KEY& key,
942 const KEY_EQUAL& equalityFunctor,
943 std::size_t hashCode)
944{
947
948 const HashTableBucket *bucket = findBucketForHashCode(anchor, hashCode);
949 BSLS_ASSERT_SAFE(bucket);
950
951 for (BidirectionalLink *cursor = bucket->first(),
952 * const end = bucket->end();
953 end != cursor; cursor = cursor->nextLink() ) {
954 if (equalityFunctor(key, extractKey<KEY_CONFIG>(cursor))) {
955 return cursor; // RETURN
956 }
957 }
958
959 return 0;
960}
961
962template <class KEY_CONFIG, class HASHER>
964 BidirectionalLink *elementList,
965 const HASHER& hasher)
966{
967 BSLS_ASSERT_SAFE(newAnchor);
969 BSLS_ASSERT_SAFE(0 != newAnchor->bucketArraySize());
970 BSLS_ASSERT_SAFE(!elementList || !elementList->previousLink());
971
972 /// An object of this proctor class guarantees that, on leaving scope,
973 /// any remaining elements in the original specified `elementList` are
974 /// spliced to the front of the list rooted in the specified `newAnchor`
975 /// so that there is only one list for the client to clear if an exception is thrown by a user supplied hash functor.
976 ///
977 /// \note Note that it
978 /// might be possible to avoid creating such a proctor in C++11 if the
979 /// hash functor is determined to be `noexcept`.
980 ///
981 /// See @ref bslalg_hashtableimputil
982 class Proctor {
983
984 private:
985 BidirectionalLink **d_sourceList;
986 HashTableAnchor *d_targetAnchor;
987
988#if !defined(BSLS_PLATFORM_CMP_MSVC) // Microsoft warns if these
989 Proctor(const Proctor&); // = delete; // methods are declared private.
990 Proctor& operator=(const Proctor&); // = delete;
991#endif
992
993 public:
994 Proctor(BidirectionalLink **sourceList,
995 HashTableAnchor *targetAnchor)
996 : d_sourceList(sourceList)
997 , d_targetAnchor(targetAnchor)
998 {
999 BSLS_ASSERT(sourceList);
1000 BSLS_ASSERT(targetAnchor);
1001 }
1002
1003 ~Proctor()
1004 {
1005 if (BidirectionalLink *lastLink = *d_sourceList) {
1006 for( ; lastLink->nextLink(); lastLink = lastLink->nextLink()) {
1007 // This loop body is intentionally left blank.
1008 }
1009 BidirectionalLinkListUtil::spliceListBeforeTarget(
1010 *d_sourceList,
1011 lastLink,
1012 d_targetAnchor->listRootAddress());
1013 }
1014 }
1015 };
1016
1017 // The callers of this function should be rewritten to take into account
1018 // that it is the responsibility of this function, not its callers, to zero
1019 // out the buckets.
1020
1021 for (void **cursor = (void **) newAnchor->bucketArrayAddress(),
1022 ** const end = (void **) (newAnchor->bucketArrayAddress() +
1023 newAnchor->bucketArraySize());
1024 cursor < end; ++cursor) {
1025 *cursor = 0;
1026 }
1027 newAnchor->setListRootAddress(0);
1028
1029 Proctor enforceSingleListOnExit(&elementList, newAnchor);
1030
1031 while (elementList) {
1032 BidirectionalLink *nextNode = elementList;
1033 elementList = elementList->nextLink();
1034
1035 insertAtBackOfBucket(newAnchor,
1036 nextNode,
1037 hasher(extractKey<KEY_CONFIG>(nextNode)));
1038 }
1039}
1040
1041template <class KEY_CONFIG, class HASHER>
1043 const HASHER& hasher,
1044 bslma::Allocator *allocator)
1045{
1046 HashTableBucket *array = anchor.bucketArrayAddress();
1047 size_t size = anchor.bucketArraySize();
1048 BidirectionalLink *root = anchor.listRootAddress();
1049
1050 if (!array || !size) {
1051 return false; // RETURN
1052 }
1053
1054 if (!root) {
1055 // An empty list, so there should be no pointers set in the bucket
1056 // array.
1057 for (size_t i = 0; i < size; ++i) {
1058 const HashTableBucket& b = array[i];
1059 if (b.first() || b.last()) {
1060 return false; // RETURN
1061 }
1062 }
1063
1064 return true; // RETURN
1065 }
1066
1067 if (!allocator) {
1069 }
1070
1071 bool *bucketsUsed = (bool *) allocator->allocate(size);
1072 bslma::DeallocatorGuard<bslma::Allocator> guard(bucketsUsed, allocator);
1073 for (size_t i = 0; i < size; ++i) {
1074 bucketsUsed[i] = false;
1075 }
1076
1077 size_t hash = hasher(extractKey<KEY_CONFIG>(root));
1078 size_t bucketIdx = computeBucketIndex(hash, size);
1079 if (array[bucketIdx].first() != root) {
1080 return false; // RETURN
1081 }
1082
1083 bucketsUsed[bucketIdx] = true;
1084
1085 BidirectionalLink *prev = root;
1086 size_t prevBucketIdx = bucketIdx;
1087 while (BidirectionalLink *cursor = prev->nextLink()) {
1088 if (cursor->previousLink() != prev) {
1089 return false; // RETURN
1090 }
1091
1092 hash = hasher(extractKey<KEY_CONFIG>(cursor));
1093 bucketIdx = computeBucketIndex(hash, size);
1094
1095 if (bucketIdx != prevBucketIdx) {
1096 // New bucket
1097
1098 // We should be the first node in the new bucket, so if this
1099 // bucket's been visited before, it's an error.
1100
1101 if (bucketsUsed[bucketIdx]) {
1102 return false; // RETURN
1103 }
1104 bucketsUsed[bucketIdx] = true;
1105
1106 // Since we're the first node in the bucket, bucket.first()
1107 // should point at us.
1108
1109 if (array[bucketIdx].first() != cursor) {
1110 return false; // RETURN
1111 }
1112
1113 // 'last()' of the previous bucket should point at the
1114 // previous node.
1115
1116 if (array[prevBucketIdx].last() != prev) {
1117 return false; // RETURN
1118 }
1119 }
1120
1121 // Set 'prev' variables for next iteration
1122 prev = cursor;
1123 prevBucketIdx = bucketIdx;
1124 }
1125
1126 if (array[prevBucketIdx].last() != prev) {
1127 return false; // RETURN
1128 }
1129
1130 // Check that traversing the root list traversed all non-empty buckets.
1131
1132 for (size_t i = 0; i < size; ++i) {
1133 const HashTableBucket& b = array[i];
1134 if (bucketsUsed[i]) {
1135 if (!b.first() || !b.last()) {
1136 return false; // RETURN
1137 }
1138 }
1139 else if ( b.first() || b.last()) {
1140 return false; // RETURN
1141 }
1142 }
1143
1144 return true;
1145}
1146
1147} // close package namespace
1148
1149
1150#endif
1151
1152// ----------------------------------------------------------------------------
1153// Copyright 2013 Bloomberg Finance L.P.
1154//
1155// Licensed under the Apache License, Version 2.0 (the "License");
1156// you may not use this file except in compliance with the License.
1157// You may obtain a copy of the License at
1158//
1159// http://www.apache.org/licenses/LICENSE-2.0
1160//
1161// Unless required by applicable law or agreed to in writing, software
1162// distributed under the License is distributed on an "AS IS" BASIS,
1163// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1164// See the License for the specific language governing permissions and
1165// limitations under the License.
1166// ----------------------------- END-OF-FILE ----------------------------------
1167
1168/** @} */
1169/** @} */
1170/** @} */
Definition bslalg_bidirectionalnode.h:357
Definition bslalg_hashtableanchor.h:542
BidirectionalLink * listRootAddress() const
Return the value listRootAddress attribute of this object.
Definition bslalg_hashtableanchor.h:695
std::size_t bucketArraySize() const
Return the value of the bucketArraySize attribute of this object.
Definition bslalg_hashtableanchor.h:701
void setListRootAddress(BidirectionalLink *value)
Definition bslalg_hashtableanchor.h:678
HashTableBucket * bucketArrayAddress() const
Definition bslalg_hashtableanchor.h:707
Definition bslma_allocator.h:545
virtual void * allocate(size_type size)=0
Definition bslma_deallocatorguard.h:166
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1976
#define BSLS_ASSERT_SAFE(X)
Definition bsls_assert.h:1917
#define BSLS_IDENT(str)
BSLS_IDENT() - insert string into .comment binary segment (if supported)
Definition bsls_ident.h:238
Definition bdlc_flathashmap.h:2218
Definition bslmf_conditional.h:123
Definition bslmf_integralconstant.h:261
Definition bslalg_hashtablebucket.h:297
BidirectionalLink * first() const
Definition bslalg_hashtablebucket.h:421
BidirectionalLink * end() const
Definition bslalg_hashtablebucket.h:415
BidirectionalLink * last() const
Definition bslalg_hashtablebucket.h:427
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
KEY_CONFIG::ValueType ValueType
Definition bslalg_hashtableimputil.h:581
@ RESULT_SELECTOR
Definition bslalg_hashtableimputil.h:598
Definition bslalg_hashtableimputil.h:615
static HashTableImpUtil_ExtractKeyResult< KEY_CONFIG >::Type extractKey(BidirectionalLink *link)
Definition bslalg_hashtableimputil.h:902
static BidirectionalLink * findTransparent(const HashTableAnchor &anchor, const LOOKUP_KEY &key, const KEY_EQUAL &equalityFunctor, std::size_t hashCode)
Definition bslalg_hashtableimputil.h:939
static KEY_CONFIG::ValueType & extractValue(BidirectionalLink *link)
Definition bslalg_hashtableimputil.h:890
static bool isWellFormed(const HashTableAnchor &anchor, const HASHER &hasher, bslma::Allocator *allocator=0)
Definition bslalg_hashtableimputil.h:1042
static bool bucketContainsLink(const HashTableBucket &bucket, BidirectionalLink *linkAddress)
Definition bslalg_hashtableimputil.h:869
static void rehash(HashTableAnchor *newAnchor, BidirectionalLink *elementList, const HASHER &hasher)
Definition bslalg_hashtableimputil.h:963
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:858
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:914
static Allocator * defaultAllocator()
Definition bslma_default.h:905