BDE 4.14.0 Production release
Loading...
Searching...
No Matches
bdlcc_stripedunorderedcontainerimpl.h
Go to the documentation of this file.
1/// @file bdlcc_stripedunorderedcontainerimpl.h
2///
3/// The content of this file has been pre-processed for Doxygen.
4///
5
6
7// bdlcc_stripedunorderedcontainerimpl.h -*-C++-*-
8#ifndef INCLUDED_BDLCC_STRIPEDUNORDEREDCONTAINERIMPL
9#define INCLUDED_BDLCC_STRIPEDUNORDEREDCONTAINERIMPL
10
11#include <bsls_ident.h>
12BSLS_IDENT("$Id: $")
13
14/// @defgroup bdlcc_stripedunorderedcontainerimpl bdlcc_stripedunorderedcontainerimpl
15/// @brief Provide common implementation of *striped* un-ordered map/multimap.
16/// @addtogroup bdl
17/// @{
18/// @addtogroup bdlcc
19/// @{
20/// @addtogroup bdlcc_stripedunorderedcontainerimpl
21/// @{
22///
23/// <h1> Outline </h1>
24/// * <a href="#bdlcc_stripedunorderedcontainerimpl-purpose"> Purpose</a>
25/// * <a href="#bdlcc_stripedunorderedcontainerimpl-classes"> Classes </a>
26/// * <a href="#bdlcc_stripedunorderedcontainerimpl-description"> Description </a>
27/// * <a href="#bdlcc_stripedunorderedcontainerimpl-thread-safety"> Thread Safety </a>
28/// * <a href="#bdlcc_stripedunorderedcontainerimpl-runtime-complexity"> Runtime Complexity </a>
29/// * <a href="#bdlcc_stripedunorderedcontainerimpl-number-of-stripes"> Number of Stripes </a>
30/// * <a href="#bdlcc_stripedunorderedcontainerimpl-rehash"> Rehash </a>
31/// * <a href="#bdlcc_stripedunorderedcontainerimpl-concurrent-rehash"> Concurrent Rehash </a>
32/// * <a href="#bdlcc_stripedunorderedcontainerimpl-rehash-control"> Rehash Control </a>
33/// * <a href="#bdlcc_stripedunorderedcontainerimpl-usage"> Usage </a>
34///
35/// # Purpose {#bdlcc_stripedunorderedcontainerimpl-purpose}
36/// Provide common implementation of *striped* un-ordered map/multimap.
37///
38/// # Classes {#bdlcc_stripedunorderedcontainerimpl-classes}
39///
40/// - bdlcc::StripedUnorderedContainerImpl: striped container for key-value types
41///
42/// @see bdlcc_stripedunorderedmap, bdlcc_stripedunorderedmultimap
43///
44/// # Description {#bdlcc_stripedunorderedcontainerimpl-description}
45/// This component provides `bdlcc::StripedUnorderedContainerImpl`,
46/// a common implementation for `bdlcc::StripedUnorderedMap` and
47/// `bdlcc::StripedUnorderedMultiMap`, that are concurrent (fully thread-safe)
48/// associative containers that partition their underlying hash tables into a
49/// (user-defined) number of "bucket groups" and control access to each part of
50/// their hash tables by separate read-write locks. For most methods, the "map"
51/// and "multimap" classes forward to the analogous method in this "impl" class
52/// with an additional argument that specifies if the calling class has unique
53/// keys or not.
54///
55/// ## Thread Safety {#bdlcc_stripedunorderedcontainerimpl-thread-safety}
56///
57///
58/// The `bdlcc::StripedUnorderedContrainerImpl` class template is fully
59/// thread-safe (see {@ref bsldoc_glossary |Fully Thread-Safe}), assuming that the
60/// allocator is fully thread-safe. Each method is executed by the calling
61/// thread.
62///
63/// ## Runtime Complexity {#bdlcc_stripedunorderedcontainerimpl-runtime-complexity}
64///
65///
66/// @code
67/// +----------------------------------------------------+--------------------+
68/// | Operation | Complexity |
69/// +====================================================+====================+
70/// | insert, setValue, setComputedValue, update | Average: O[1] |
71/// | | Worst: O[n] |
72/// +----------------------------------------------------+--------------------+
73/// | erase, getValue | Average: O[1] |
74/// | | Worst: O[n] |
75/// +----------------------------------------------------+--------------------+
76/// | visit(key, visitor) | Average: O[1] |
77/// | visitReadOnly(key, visitor) | Worst: O[n] |
78/// +----------------------------------------------------+--------------------+
79/// | insertBulk, k elements | Average: O[k] |
80/// | | Worst: O[n*k] |
81/// +----------------------------------------------------+--------------------+
82/// | eraseBulk, k elements | Average: O[k] |
83/// | | Worst: O[n*k] |
84/// +----------------------------------------------------+--------------------+
85/// | rehash | O[n] |
86/// +----------------------------------------------------+--------------------+
87/// | visit(visitor), visitReadOnly(visitor) | O[n] |
88/// +----------------------------------------------------+--------------------+
89/// @endcode
90///
91/// ## Number of Stripes {#bdlcc_stripedunorderedcontainerimpl-number-of-stripes}
92///
93///
94/// Performance improves monotonically when the number of stripes increases.
95/// However, the rate of improvement decreases, and reaches a plateau. The
96/// plateau is reached roughly at four times the number of the threads
97/// *concurrently* using the hash map.
98///
99/// ## Rehash {#bdlcc_stripedunorderedcontainerimpl-rehash}
100///
101///
102///
103/// ### Concurrent Rehash {#bdlcc_stripedunorderedcontainerimpl-concurrent-rehash}
104///
105///
106/// A rehash operation is a re-organization of the hash map to a different
107/// number of buckets. This is a heavy operation that interferes with, but does
108/// *not* disallow, other operations on the container. Rehash is warranted when
109/// the current load factor exceeds the current maximum allowed load factor.
110/// Expressed explicitly:
111/// @code
112/// bucketCount() <= maxLoadFactor() * size();
113/// @endcode
114/// This above condition is tested implicitly by several methods and if found
115/// true (and if rehash is enabled and rehash is not underway), a rehash is
116/// started. The methods that check the load factor are:
117///
118/// * All methods that insert elements (i.e., increase `size()`).
119/// * The `maxLoadFactor(newMaxLoadFactor)` method.
120/// * The `rehash` method.
121///
122/// ### Rehash Control {#bdlcc_stripedunorderedcontainerimpl-rehash-control}
123///
124///
125/// `enableRehash` and `disableRehash` methods are provided to control the
126/// rehash enable flag. Note that disabling rehash does not impact a rehash in
127/// progress.
128///
129/// ## Usage {#bdlcc_stripedunorderedcontainerimpl-usage}
130///
131///
132/// There is no usage example for this component since it is not meant for
133/// direct client use.
134/// @}
135/** @} */
136/** @} */
137
138/** @addtogroup bdl
139 * @{
140 */
141/** @addtogroup bdlcc
142 * @{
143 */
144/** @addtogroup bdlcc_stripedunorderedcontainerimpl
145 * @{
146 */
147
148#include <bdlscm_version.h>
149
151
152#include <bslim_printer.h>
153
154#include <bslstl_hash.h>
155#include <bslstl_pair.h>
156
157#include <bslma_allocator.h>
159#include <bslma_default.h>
164
165#include <bslmf_movableref.h>
167
169#include <bslmt_readlockguard.h>
170#include <bslmt_writelockguard.h>
171
172#include <bsls_assert.h>
173#include <bsls_atomic.h>
174#include <bsls_libraryfeatures.h>
175#include <bsls_objectbuffer.h>
176#include <bsls_platform.h> // BSLS_PLATFORM_CPU_X86_64
177
178#include <bsl_algorithm.h>
179#include <bsl_cstddef.h> // 'NULL'
180#include <bsl_functional.h>
181#include <bsl_list.h>
182#include <bsl_vector.h>
183#include <bsl_iostream.h>
184#include <bsl_sstream.h>
185
186#include <vector>
187
188
189namespace bdlcc {
190
191template <class KEY,
192 class VALUE,
193 class HASH = bsl::hash<KEY>,
194 class EQUAL = bsl::equal_to<KEY> >
195class StripedUnorderedContainerImpl;
196
197 // ========================================
198 // class StripedUnorderedContainerImpl_Node
199 // ========================================
200
201/// This class template represents a node in the singly-linked list of
202/// `(KEY, VALUE)` elements for each bucket of a hash map.
203///
204/// See @ref bdlcc_stripedunorderedcontainerimpl
205template <class KEY, class VALUE>
207
208 private:
209 // DATA
210
211 // Pointer to next element of the bucket
212 mutable StripedUnorderedContainerImpl_Node *d_next_p;
213
214 // footprint of key
216
217 // Footprint of value
219
220 // memory allocator (held, not owned).
221 bslma::Allocator *d_allocator_p;
222
223 // NOT IMPLEMENTED
226 // = delete
229 // = delete
230
231 public:
232 // CREATORS
233
235 const KEY& key,
236 const VALUE& value,
238 bslma::Allocator *basicAllocator = 0);
239 /// Create a `bdlcc::StripedUnorderedContainerImpl_Node` object having
240 /// the specified `key` and `value`, and with the specified `nextPtr`
241 /// pointer to the next node. Optionally specify a `basicAllocator`
242 /// used to supply memory. If `basicAllocator` is 0, the currently
243 /// installed default allocator is used.
245 const KEY& key,
248 bslma::Allocator *basicAllocator = 0);
249
250 /// Create a `bdlcc::StripedUnorderedContainerImpl_Node` object having
251 /// the specified `key` and a value initialized to `VALUE()`, and with
252 /// the specified `nextPtr` pointer to the next node. Optionally
253 /// specify a `basicAllocator` used to supply memory. If
254 /// `basicAllocator` is 0, the currently installed default allocator is
255 /// used.
257 const KEY& key,
259 bslma::Allocator *basicAllocator = 0);
260
261 /// Destroy this object.
263
264 // MANIPULATORS
265
266 /// Return the address of the pointer to the next node.
268
269 /// Set this node's pointer-to-next-node to the specified `nextPtr`.
271
272 /// Return a reference providing modifiable access to the `value`
273 /// attribute of this object.
274 VALUE& value();
275
276 // ACCESSORS
277
278 /// Return a `const` reference to the `key` attribute of this object.
279 const KEY& key() const;
280
281 /// Return the pointer to the next node.
283
284 /// Return a `const` reference to the `value` attribute of this object.
285 const VALUE& value() const;
286
287 // Aspects
288
289 /// Return the allocator used by `StripedUnorderedContainerImpl_Node` to
290 /// allocate memory.
292};
293
294 // ==========================================
295 // class StripedUnorderedContainerImpl_Bucket
296 // ==========================================
297
298/// This class represents a bucket of the hash map. This class template
299/// represents the head of in the singly-linked list of `(KEY, VALUE)`
300/// elements in a hash map.
301///
302/// See @ref bdlcc_stripedunorderedcontainerimpl
303template <class KEY, class VALUE>
305
306 private:
307 // PRIVATE TYPES
308
309 /// This `typedef` is a convenient alias for the utility associated with
310 /// movable references.
311 typedef BloombergLP::bslmf::MovableRefUtil MoveUtil;
312
313 // DATA
314
315 // Pointer to the first element in the bucket
317
318 // Pointer to the last element in the bucket
320
321 // Number of nodes in this bucket
322 bsl::size_t d_size;
323
324 // memory allocator (held, not owned).
325 bslma::Allocator *d_allocator_p;
326
327 // NOT IMPLEMENTED
332
333 public:
334 // TYPES
335
336 /// Enumeration to differentiate between processing all elements with
337 /// the same key, or just the first one (and typically the only one).
339
340 e_BUCKETSCOPE_FIRST = 0, // Act on first matching element found.
341 e_BUCKETSCOPE_ALL // Act on all matching elements.
342 };
343
344 // TRAITS
347
348 // CREATORS
349
350 /// Create an empty `bdlcc::StripedUnorderedContainerImpl_Bucket`
351 /// object. Optionally specify a `basicAllocator` used to supply
352 /// memory. If `basicAllocator` is 0, the currently installed default
353 /// allocator is used.
355 bslma::Allocator *basicAllocator = 0);
356
357 /// Create a `bdlcc::StripedUnorderedContainerImpl_Bucket` object having
358 /// the same value as the specified `original` object. The `original`
359 /// object is left in a valid but unspecified state.
362 original,
364
365 /// Destroy this object.
367
368 // MANIPULATORS
369
370 /// Add the specified `nodePtr` node at the end of this bucket.
372
373 /// Empty `StripedUnorderedContainerImpl_Bucket` and delete all nodes.
374 void clear();
375
376 /// Return the address of the head (node) of this bucket list.
378
379 /// Increment the `size` attribute of this bucket by the specified
380 /// `amount`.
381 void incrementSize(int amount);
382
383 /// Set the address of the head of this bucket list to the specified
384 /// `value`.
386
387 /// Set the `size` attribute of this bucket to the specified `value`.
388 void setSize(bsl::size_t value);
389
390 /// Set the pointer of the tail of this bucket list to the specified
391 /// `value`.
393
394 /// Set the value attribute of the element in this bucket having the
395 /// specified `key` to the specified `value`, using the specified
396 /// `equal` to compare keys. If no such element exists, insert
397 /// `(key, value)`. The behavior with respect to duplicate key values
398 /// in the bucket depends on the specified `scope`:
399 ///
400 ///: `e_BUCKETSCOPE_ALL`:
401 ///: Set `value` to every element in the bucket having `key`.
402 ///:
403 ///: `e_BUCKETSCOPE_FIRST`:
404 ///: Set `value` to the first element found having `key`.
405 ///
406 /// Return the number of elements found having `key` that had their
407 /// value set. Note that, when there are multiple elements having
408 /// `key`, the selection of "first" is unspecified and subject to
409 /// change. Also note that specifying `e_BUCKETSCOPE_FIRST` is more
410 /// performant when there is a single element in the bucket having
411 /// `key`.
412 template <class EQUAL>
413 bsl::size_t setValue(const KEY& key,
414 const EQUAL& equal,
415 const VALUE& value,
416 BucketScope scope);
417
418 /// Set the value attribute of the element in this bucket having the
419 /// specified `key` to the specified `value`, using the specified
420 /// `equal` to compare keys. If no such element exists, insert
421 /// `(key, value)`. If there are multiple elements in this hash map
422 /// having `key` then set the value of the first such element found.
423 /// Return the number of elements found having `key` that had their
424 /// value set. Note that, when there are multiple elements having
425 /// `key`, the selection of "first" is unspecified and subject to
426 /// change.
427 template <class EQUAL>
428 bsl::size_t setValue(const KEY& key,
429 const EQUAL& equal,
431
432 // ACCESSORS
433
434 /// Return `true` if this bucket contains no elements, and `false`
435 /// otherwise.
436 bool empty() const;
437
438 /// Return the head (node) of this bucket list.
440
441 /// Return the current number of elements in this bucket.
442 bsl::size_t size() const;
443
444 /// Return address of the tail (node) of this bucket list.
446
447 // Aspects
448
449 /// Return the allocator used by `StripedUnorderedContainerImpl_Bucket`
450 /// to allocate memory.
452};
453
454template <class KEY,
455 class VALUE,
456 class HASH = bsl::hash<KEY>,
457 class EQUAL = bsl::equal_to<KEY> >
459
463
464 // ===================================
465 // class StripedUnorderedContainerImpl
466 // ===================================
467
468/// This class implements the logic for a striped hash multimap with logic
469/// that supports a (unique) map as a special case.
470///
471/// See @ref bdlcc_stripedunorderedcontainerimpl
472template <class KEY, class VALUE, class HASH, class EQUAL>
474
475 public:
476 // TYPES
477 enum {
478 k_DEFAULT_NUM_BUCKETS = 16, // Default # of buckets
479 k_DEFAULT_NUM_STRIPES = 4 // Default # of stripes
480 };
481
482 /// Node in a bucket.
484
485 /// Value type of a bulk insert entry.
487
488 /// An alias to a function meeting the following contract:
489 /// @code
490 /// bool visitorFunction(VALUE *value, const KEY& key);
491 /// // Visit the specified 'value' attribute associated with the
492 /// // specified 'key'. Return 'true' if this function may be
493 /// // called on additional elements, and 'false' otherwise (i.e.,
494 /// // if no other elements should be visited). Note that this
495 /// // functor can change the value associated with 'key'.
496 /// @endcode
497 typedef bsl::function<bool (VALUE *, const KEY&)> VisitorFunction;
498
499 /// An alias to a function meeting the following contract:
500 /// @code
501 /// bool visitorFunction(const VALUE& value, const KEY& key);
502 /// // Visit the specified 'value' attribute associated with the
503 /// // specified 'key'. Return 'true' if this function may be
504 /// // called on additional elements, and 'false' otherwise (i.e.,
505 /// // if no other elements should be visited). Note that this
506 /// // functor can *not* change the values associated with 'key'
507 /// // and 'value'.
508 /// @endcode
509 typedef bsl::function<bool (const VALUE&, const KEY&)>
511
512 /// An alias to a function meeting the following contract:
513 /// @code
514 /// bool eraseIfValuePredicate(const VALUE& value);
515 /// // Return 'true' if the specified 'value' is to be removed from
516 /// // the container, and 'false' otherwise. Note that this
517 /// // functor can *not* change the values associated with 'value'.
518 /// @endcode
519 typedef bsl::function<bool(const VALUE&)> EraseIfValuePredicate;
520
521 private:
522 // PRIVATE CONSTANTS
523 static const int k_REHASH_IN_PROGRESS = 1; // d_state bit 0
524 static const int k_REHASH_ENABLED = 2; // d_state bit 1
525
526 // PRIVATE TYPES
527 enum {
528 #if BSLS_PLATFORM_CPU_X86 || BSLS_PLATFORM_CPU_X86_64
529 k_PREFETCH_ENABLED = 1,
530 #else
531 k_PREFETCH_ENABLED = 0,
532 #endif
533 // Can be 0 or 1; if prefetch, we use 2 cachelines at a time
534 k_EFFECTIVE_CACHELINE_SIZE = (1 + k_PREFETCH_ENABLED) *
536 // Cacheline size to use; may be 1 or 2 cachelines
537 k_INT_PADDING = k_EFFECTIVE_CACHELINE_SIZE - sizeof(bsls::AtomicInt)
538 };
539
540 enum Multiplicity {
541 // Enumeration to differentiate between inserting only unique keys and
542 // inserting multiple values for the same key.
543
544 e_INSERT_UNIQUE = 0, // Insert a new element having 'key' if no element
545 // in hash map has 'key'; otherwise, update the
546 // value attribute of the existing element.
547
548 e_INSERT_ALWAYS // Insert a new element having 'key' even if the
549 // map already has an element(s) having the 'key'
550 // attribute.
551 };
552
553 enum Scope {
554 // Enumeration to differentiate between processing all elements with
555 // the same key, or just the first one (and typically the only one).
556
557 e_SCOPE_FIRST = 0, // Act on first matching element found.
558 e_SCOPE_ALL // Act on all matching elements.
559 };
560
562 typedef StripedUnorderedContainerImpl_LockElementReadGuard LERGuard;
563 typedef StripedUnorderedContainerImpl_LockElementWriteGuard LEWGuard;
564
565 // DATA
566
567 // number of stripes
568 bsl::size_t d_numStripes;
569
570 // number of buckets
571 bsl::size_t d_numBuckets;
572
573 // d_numStripes - 1; this value is used to provide an efficient modulo
574 // (using bit-wise `&`) of d_numStripes (where d_numStripes must be a
575 // power of 2).
576 bsl::size_t d_hashMask;
577
578 // maxLoadFactor (defaults to 1.0)
579 float d_maxLoadFactor;
580
581 // hashing function for keys
582 HASH d_hasher;
583
584 // comparison function for keys
585 EQUAL d_comparator;
586
587 // * bit 0: 0-rehash not in progress; 1-rehash in progress.
588 // * bit 1: 0-rehash disabled; 1-rehash enabled.
589 mutable bsls::AtomicInt d_state;
590
591 // padding, so that `d_state` will have its own cache line
592 const char d_statePad[k_INT_PADDING];
593
594 // # of elements in the hash map
595 bsls::AtomicInt d_numElements;
596
597 // padding, so that `d_numElements` will have its own cache line
598 const char d_numElementsPad[k_INT_PADDING];
599
600 // hash table data, storing key-value pairs
602 d_buckets;
603
604 // Pointer to an array of locks for the stripes. Note that mutex can't
605 // be moved or copied, hence can't be in a vector.
606 LockElement *d_locks_p;
607
608 // memory allocator (held, not owned)
609 bslma::Allocator *d_allocator_p;
610
611 // FRIENDS
612 friend class
615
616 // NOT IMPLEMENTED
619 // = delete
622 // = delete
623
624 // PRIVATE CLASS METHODS
625
626 /// Return the number of buckets needed by this implementation for the
627 /// specified `numBuckets` and `numStripes`. That value is the lowest
628 /// integer that is a power of 2 that is greater than or equal
629 /// `numBuckets`, `numStripes`, and 2.
630 static bsl::size_t adjustBuckets(bsl::size_t numBuckets,
631 bsl::size_t numStripes);
632
633 /// Return `true`
634 static bool alwaysTrue(const VALUE&);
635
636 /// Return the nearest higher power of 2 for the specified `num`.
637 static bsl::size_t powerCeil(bsl::size_t num);
638
639 // PRIVATE MANIPULATORS
640
641 /// Perform a rehash if the `loadFactor() > maxLoadFactor()`, and
642 /// `true == canRehash()`.
643 void checkRehash();
644
645 /// Remove from this hash map the element, if any, having the specified
646 /// `key`. If there a multiple elements having `key` and the specified
647 /// `scope` is `e_SCOPE_ALL`, erase them all; otherwise; erase just the
648 /// first element found. Return the number of elements erased. Note
649 /// that, when there are multiple elements having `key`, the selection
650 /// of "first" is unspecified and subject to change.
651 bsl::size_t erase(const KEY& key, Scope scope);
652
653 /// Erase from this hash map elements in this hash map having any of the
654 /// values in the keys contained between the specified `first`
655 /// (inclusive) and `last` (exclusive) random-access iterators. The
656 /// iterators provide read access to a sequence of `KEY` objects. If
657 /// there are multiple elements for any key value and the specified
658 /// `scope` is `e_SCOPE_ALL` then erase them all; otherwise, erase just
659 /// the first such element found. Return the number of elements erased.
660 /// The behavior is undefined unless `first <= last`. Note that, when
661 /// there are multiple elements having `key`, the selection of "first"
662 /// is unspecified and subject to change.
663 template <class RANDOM_ITER>
664 bsl::size_t eraseBulk(RANDOM_ITER first,
665 RANDOM_ITER last,
666 Scope scope);
667
668 /// Remove from this hash map the element, if any, having the specified
669 /// `key`, where specified `predicate` holds true. If there are
670 /// multiple elements having the `key` for which the `predicate` holds
671 /// true and the specified `scope` is `e_SCOPE_ALL`, erase them all;
672 /// otherwise, erase just the first element found. Return the number of
673 /// elements erased. Note that, when there are multiple elements for
674 /// which `predicate` holds true, the selection of "first" is
675 /// unspecified and subject to change.
676 bsl::size_t eraseIf(const KEY& key,
677 Scope scope,
678 const EraseIfValuePredicate& predicate);
679
680 /// Insert into this hash map an element having the specified `key` and
681 /// `value`. The behavior with respect to duplicate key values in the
682 /// hash map depends on the specified `multiplicity`:
683 ///
684 ///: `e_INSERT_ALWAYS`:
685 ///: The insertion occurs irrespective of other elements in the hash
686 ///: map having the same `key` value.
687 ///:
688 ///: `e_INSERT_UNIQUE`:
689 ///: Insert a new element if no element in the hash map has the `key`
690 ///: value; otherwise, update the value attribute of the first element
691 ///: found having `key` to `value`.
692 ///
693 /// Return the number of elements inserted. Note that, when there are
694 /// multiple elements having `key`, the selection of "first" is
695 /// unspecified and subject to change.
696 bsl::size_t insert(const KEY& key,
697 const VALUE& value,
698 Multiplicity multiplicity);
699 bsl::size_t insert(const KEY& key,
701 Multiplicity multiplicity);
702
703 /// Insert into this hash map elements having the key-value pairs
704 /// obtained between the specified `first` (inclusive) and `last`
705 /// (exclusive) random-access iterators. The iterators provide read
706 /// access to a sequence of `bsl::pair<KEY, VALUE>` objects. The
707 /// behavior with respect to duplicate key values in the hash map
708 /// depends on the specified `multiplicity`:
709 ///
710 ///: `e_INSERT_ALWAYS`:
711 ///: The insertion occurs irrespective of other elements in the hash
712 ///: map having the same `key` value. Note that this is the only
713 ///: way to adding elements with non-unique keys to the hash map.
714 ///:
715 ///: `e_INSERT_UNIQUE`:
716 ///: Insert a new element if no element in the hash map has the `key`
717 ///: value; otherwise, update the value attribute of the first element
718 ///: found having `key` to `value`.
719 ///
720 /// Return the number of elements inserted. The behavior is undefined
721 /// unless `first <= last`. Note that, when there are multiple elements
722 /// having `key`, the selection of "first" is unspecified and subject to
723 /// change.
724 template <class RANDOM_ITER>
725 bsl::size_t insertBulk(RANDOM_ITER first,
726 RANDOM_ITER last,
727 Multiplicity multiplicity);
728
729 /// Invoke the specified `visitor` passing the specified `key`, and the
730 /// address of the attribute part of an element (of possibly many
731 /// elements) found in this hash map having `key`. That is, for
732 /// `(key, value)`, invoke:
733 /// @code
734 /// bool visitor(&value, key);
735 /// @endcode
736 /// If no element in the hash map has `key`, insert `(key, VALUE())` and
737 /// invoke to `visitor` with `value` pointing to the defsult constructed
738 /// value. If there are multiple elements having `key` and the
739 /// specified `scope` is `e_SCOPE_ALL` then apply `visitor` to each of
740 /// them; otherwise, `visitor` is applied to just the first such element
741 /// found. Return the number of elements visited or the negation of
742 /// that value if visitations stopped because `visitor` returned
743 /// `false`. `visitor` has exclusive access (i.e., write access) to the
744 /// element. The behavior is undefined if hash map manipulators and
745 /// `getValue*` methods are invoked from within `visitor`, as it may
746 /// lead to a deadlock. Note that, when there are multiple elements
747 /// having `key`, the selection of "first" and the order applying
748 /// `visitor` are unspecified and subject to change. Also note that a
749 /// return value of `0` implies that an element was inserted.
750 int setComputedValue(const KEY& key,
751 const VisitorFunction& visitor,
752 Scope scope);
753
754 /// Set the value attribute of the element in this hash map having the
755 /// specified `key` to the specified `value`. If no such element
756 /// exists, insert `(key, value)`. The behavior with respect to
757 /// duplicate key values in the bucket depends on the specified
758 /// `scope`:
759 ///
760 ///: `e_SCOPE_ALL`:
761 ///: Set `value` to every element in the bucket having `key`.
762 ///:
763 ///: `e_SCOPE_FIRST`:
764 ///: Set `value` to the first element found having `key`.
765 ///
766 /// Return the number of elements found having `key`. Note that if no
767 /// elements were found, and a new value was inserted, `0` is returned.
768 /// Also note that, when there are multiple elements having `key`, the
769 /// selection of "first" is unspecified and subject to change. Also
770 /// note that specifying `e_SCOPE_FIRST` is more performant when there
771 /// is a single element in the bucket having `key`.
772 bsl::size_t setValue(const KEY& key,
773 const VALUE& value,
774 Scope scope);
775
776 // PRIVATE ACCESSORS
777
778 /// Return the index of the bucket, in the array of buckets maintained
779 /// by this hash map, where values having a key equivalent to the
780 /// specified `key` would be inserted using the specified `numBuckets`.
781 /// This operation does not lock the related stripe or check the rehash
782 /// state. Note that `numBuckets` does not have to be the current
783 /// number of buckets in this hash map.
784 bsl::size_t bucketIndex(const KEY& key, bsl::size_t numBuckets) const;
785
786 /// Return the stripe index associated with the specified `bucketIndex`.
787 bsl::size_t bucketToStripe(bsl::size_t bucketIndex) const;
788
789 /// Load, into the specified `*valuesPtr`, the value attributes of every
790 /// element in this hash map having the specified `key`. Return the
791 /// number of elements found with `key`. Note that the order of the
792 /// values returned is not specified.
793 template <class VECTOR>
794 bsl::size_t getValueImpl(VECTOR *valuesPtr, const KEY& key) const;
795
796 /// Lock for read the stripe related to the specified `key`, setting the
797 /// specified `bucketIdx` to the bucket index associated with `key`.
798 /// Return the address to the lock-element associated with the returned
799 /// `bucketIdx`.
800 LockElement *lockRead(bsl::size_t *bucketIdx, const KEY& key) const;
801
802 /// Lock for write the stripe related to the specified `key`, setting
803 /// the specified `bucketIdx` to the bucket index associated with `key`.
804 /// Return the address to the lock-element associated with the returned
805 /// `bucketIdx`.
806 LockElement *lockWrite(bsl::size_t *bucketIdx, const KEY& key) const;
807
808 public:
809 // CREATORS
810
811 /// Create an empty `StripedUnorderedContainerImpl` object, a fully
812 /// thread-safe hash map where access is divided into "stripes" (a group
813 /// of buckets protected by a reader-write mutex). Optionally specify
814 /// `numInitialBuckets` and `numStripes` which define the minimum number
815 /// of buckets and the (fixed) number of stripes in this map.
816 /// Optionally specify a `basicAllocator` used to supply memory. If
817 /// `basicAllocator` is 0, the currently installed default allocator is
818 /// used. The hash map has rehash enabled.
820 bsl::size_t numInitialBuckets = k_DEFAULT_NUM_BUCKETS,
821 bsl::size_t numStripes = k_DEFAULT_NUM_STRIPES,
822 bslma::Allocator *basicAllocator = 0);
823
824 /// Destroy this hash map. This method is *not* thread-safe.
826
827 // MANIPULATORS
828
829 /// Remove all elements from this striped hash map. If rehash is in
830 /// progress, block until it completes.
831 void clear();
832
833 /// Prevent rehash until the `enableRehash` method is called.
835
836 /// Allow rehash. If conditions warrant, rehash will be started by the
837 /// *next* method call that observes the load factor is exceeded (see
838 /// {Concurrent Rehash}). Note that calling
839 /// `maxLoadFactor(maxLoadFactor())` (i.e., setting the maximum load
840 /// factor to its current value) will trigger a rehash if needed but
841 /// otherwise does not change the hash map.
843
844 /// Erase from this hash map the elements having the specified `key`.
845 /// Return the number of elements erased.
846 bsl::size_t eraseAll(const KEY& key);
847
848 /// Erase from this hash map the elements having the specified `key` for
849 /// which the specified `predicate` holds true. Return the number of
850 /// elements erased.
851 bsl::size_t eraseAllIf(const KEY& key,
852 const EraseIfValuePredicate& predicate);
853
854 /// Erase from this hash map elements in this hash map having any of the
855 /// values in the keys contained between the specified `first`
856 /// (inclusive) and `last` (exclusive) random-access iterators. The
857 /// iterators provide read access to a sequence of `KEY` objects. All
858 /// erasures are done by the calling thread and the order of erasure is
859 /// not specified. Return the number of elements removed. The behavior
860 /// is undefined unless `first <= last`. Note that the map may not have
861 /// an element for every value in `keys`.
862 template <class RANDOM_ITER>
863 bsl::size_t eraseBulkAll(RANDOM_ITER first, RANDOM_ITER last);
864
865 /// Erase from this hash map elements in this hash map having any of the
866 /// values in the keys contained between the specified `first`
867 /// (inclusive) and `last` (exclusive) random-access iterators. The
868 /// iterators provide read access to a sequence of `KEY` objects. If
869 /// there are multiple elements for any key value, erase just the first
870 /// such element found. All erasures are done by the calling thread and
871 /// the order of erasure is not specified. Return the number of
872 /// elements removed. The behavior is undefined unless `first <= last`.
873 /// Note that the map may not have an element for every value in `keys`.
874 template <class RANDOM_ITER>
875 bsl::size_t eraseBulkFirst(RANDOM_ITER first, RANDOM_ITER last);
876
877 /// Erase from this hash map the *first* element (of possibly many)
878 /// found to the specified `key`. Return the number of elements erased.
879 /// Note that method is more performant than `eraseAll` when there is
880 /// one element having `key`.
881 bsl::size_t eraseFirst(const KEY& key);
882
883 /// Erase from this hash map the *first* element with specified `key`
884 /// (of possibly many) found, for which the specified `predicate` holds
885 /// true. Return the number of elements erased.
886 bsl::size_t eraseFirstIf(const KEY& key,
887 const EraseIfValuePredicate& predicate);
888
889 /// Insert into this hash map an element having the specified `key` and
890 /// `value`. Note that other elements having the same `key` may exist
891 /// in this hash map.
892 void insertAlways(const KEY& key, const VALUE& value);
893
894 /// Insert into this hash map an element having the specified `key` and
895 /// the specified move-insertable `value`. The `value` object is left
896 /// in a valid but unspecified state. If `value` is allocator-enabled
897 /// and `allocator() != value.allocator()` this operation may cost as
898 /// much as a copy. Note that other elements having the same `key` may
899 /// exist in this hash map.
900 void insertAlways(const KEY& key, bslmf::MovableRef<VALUE> value);
901
902 /// Insert into this hash map elements having the key-value pairs
903 /// obtained between the specified `first` (inclusive) and `last`
904 /// (exclusive) random-access iterators. The iterators provide read
905 /// access to a sequence of `bsl::pair<KEY, VALUE>` objects. All
906 /// insertions are done by the calling thread and the order of insertion
907 /// is not specified. The behavior is undefined unless `first <= last`.
908 template <class RANDOM_ITER>
909 void insertBulkAlways(RANDOM_ITER first, RANDOM_ITER last);
910
911 /// Insert into this hash map elements having the key-value pairs
912 /// obtained between the specified `first` (inclusive) and `last`
913 /// (exclusive) random-access iterators. The iterators provide read
914 /// access to a sequence of `bsl::pair<KEY, VALUE>` objects. If an
915 /// element having one of the keys already exists in this hash map, set
916 /// the value attribute to the corresponding value from `data`. All
917 /// insertions are done by the calling thread and the order of insertion
918 /// is not specified. Return the number of elements inserted. The
919 /// behavior is undefined unless `first <= last`.
920 template <class RANDOM_ITER>
921 bsl::size_t insertBulkUnique(RANDOM_ITER first, RANDOM_ITER last);
922
923 /// Insert into this hash map an element having the specified `key` and
924 /// `value`. If `key` already exists in this hash map, the value
925 /// attribute of that element is set to `value`. Return 1 if an element
926 /// is inserted, and 0 if an existing element is updated. Note that the
927 /// return value equals the number of elements inserted.
928 bsl::size_t insertUnique(const KEY& key, const VALUE& value);
929
930 /// Insert into this hash map an element having the specified `key` and
931 /// the specified move-insertable `value`. If `key` already exists in
932 /// this hash map, the value attribute of that element is set to
933 /// `value`. Return 1 if an element is inserted, and 0 if an existing
934 /// element is updated. The `value` object is left in a valid but
935 /// unspecified state. If `value` is allocator-enabled and
936 /// `allocator() != value.allocator()` this operation may cost as much
937 /// as a copy. Note that the return value equals the number of elements
938 /// inserted.
939 bsl::size_t insertUnique(const KEY& key, bslmf::MovableRef<VALUE> value);
940
941 /// Set the maximum load factor of this hash map to the specified
942 /// `newMaxLoadFactor`. If `newMaxLoadFactor < loadFactor()`, this
943 /// operation will cause an immediate rehash; otherwise, this operation
944 /// has a constant-time cost. The rehash will increase the number of
945 /// buckets by a power of 2. The behavior is undefined unless
946 /// `0 < newMaxLoadFactor`.
947 void maxLoadFactor(float newMaxLoadFactor);
948
949 /// Recreate this hash map to one having at least the specified
950 /// `numBuckets`. This operation is a no-op if *any* of the following
951 /// are true: 1) rehash is disabled; 2) `numBuckets` less or equals the
952 /// current number of buckets. See {Rehash}.
953 void rehash(bsl::size_t numBuckets);
954
955 /// Serially invoke the specified `visitor` passing the specified `key`,
956 /// and the address of the value of each element in this hash map having
957 /// `key`. If `key` is not in the map, `value` will be default
958 /// constructed. That is, for each `(key, value)` found, invoke:
959 /// @code
960 /// bool visitor(VALUE *value, const Key& key);
961 /// @endcode
962 /// If no element in the map has `key`, insert `(key, VALUE())` and
963 /// invoke `visitor` with `value` pointing to the defsult constructed
964 /// value. Return the number of elements visited or the negation of
965 /// that value if visitations stopped because `visitor` returned
966 /// `false`. `visitor`, when invoked, has exclusive access (i.e., write
967 /// access) to each element during each invocation. The behavior is
968 /// undefined if hash map manipulators and `getValue*` methods are
969 /// invoked from within `visitor`, as it may lead to a deadlock. Note
970 /// that the `setComputedValueFirst` method is more performant than the
971 /// when the hash map contains a single element for `key`. Also note
972 /// that a return value of `0` implies that an element was inserted.
973 int setComputedValueAll(const KEY& key,
974 const VisitorFunction& visitor);
975
976 /// Invoke the specified `visitor` passing the specified `key`, and the
977 /// address of the value attribute of the *first* element (of possibly
978 /// many elements) found in this hash map having `key`. If `key` is not
979 /// in the map, `value` will be default constructed. That is, for
980 /// `(key, value)`, invoke:
981 /// @code
982 /// bool visitor(VALUE *value, const Key& key);
983 /// @endcode
984 /// If no element in the map has `key`, insert `(key, VALUE())` and
985 /// invoke `visitor` with `value` pointing to the defsult constructed
986 /// value. Return 1 if `key` was found and `visitor` returned `true`, 0
987 /// if `key` was not found, and -1 if `key` was found and `visitor`
988 /// returned `false`. `visitor`, when invoked, has exclusive access
989 /// (i.e., write access) to the element. The behavior is undefined if
990 /// hash map manipulators and `getValue*` methods are invoked from
991 /// within `visitor`, as it may lead to a deadlock. Note that the
992 /// return value equals the number of elements inserted. Also note
993 /// that, when there are multiple elements having `key`, the selection
994 /// of "first" is implementation specific and subject to change. Also
995 /// note that this method is more performant than the
996 /// `setComputedValueAll` method when the hash map contains a single
997 /// element for `key`. Also note that a return value of `0` implies
998 /// that an element was inserted.
999 int setComputedValueFirst(const KEY& key,
1000 const VisitorFunction& visitor);
1001
1002 /// Set the value attribute of every element in this hash map having the
1003 /// specified `key` to the specified `value`. If no such such element
1004 /// exists, insert `(key, value)`. Return the number of elements found
1005 /// with `key`. Note that if no elements were found, and a new value
1006 /// was inserted, `0` is returned.
1007 bsl::size_t setValueAll(const KEY& key, const VALUE& value);
1008
1009 /// Set the value attribute of the *first* element in this hash map (of
1010 /// possibly many) found to have the specified `key` to the specified
1011 /// `value`. If no such such element exists, insert `(key, value)`.
1012 /// Return the number of elements found with `key`. Note that if no
1013 /// elements were found, and a new value was inserted, `0` is returned.
1014 /// Also note that this method is more performant than `setValueAll`
1015 /// when there is one element having `key` in the hash map.
1016 bsl::size_t setValueFirst(const KEY& key, const VALUE& value);
1017
1018 /// Set the value attribute of the element in this hash map having the
1019 /// specified `key` to the specified `value`. If no such element
1020 /// exists, insert `(key, value)`. If there are multiple elements in
1021 /// this hash map having `key` then set the value of the first such
1022 /// element found. Return the number of elements found having `key`.
1023 /// Note that if no elements were found, and a new value was inserted,
1024 /// `0` is returned. Also note that, when there are multiple elements
1025 /// having `key`, the selection of "first" is unspecified and subject to
1026 /// change.
1027 bsl::size_t setValueFirst(const KEY& key, bslmf::MovableRef<VALUE> value);
1028
1029 /// @deprecated Use @ref visit(key, visitor) instead.
1030 ///
1031 /// Serially call the specified `visitor` on each element (if one
1032 /// exists) in this hash map having the specified `key` until every such
1033 /// element has been updated or `visitor` returns `false`. That is, for
1034 /// `(key, value)`, invoke:
1035 /// @code
1036 /// bool visitor(&value, key);
1037 /// @endcode
1038 /// Return the number of elements visited or the negation of that value
1039 /// if visitations stopped because `visitor` returned `false`.
1040 /// `visitor` has exclusive access (i.e., write access) to each element
1041 /// for duration of each invocation. The behavior is undefined if hash
1042 /// map manipulators and `getValue*` methods are invoked from within
1043 /// `visitor`, as it may lead to a deadlock.
1044 int update(const KEY& key, const VisitorFunction& visitor);
1045
1046 /// Call the specified `visitor` (in an unspecified order) on the
1047 /// elements in this hash table until each element has been visited or
1048 /// `visitor` returns `false`. That is, for `(key, value)`, invoke:
1049 /// @code
1050 /// bool visitor(&value, key);
1051 /// @endcode
1052 /// Return the number of elements visited or the negation of that value
1053 /// if visitations stopped because `visitor` returned `false`.
1054 /// `visitor` has exclusive access (i.e., write access) to each element
1055 /// for duration of each invocation. Every element present in this hash
1056 /// map at the time `visit` is invoked will be visited unless it is
1057 /// removed before `visitor` is called for that element. Each
1058 /// visitation is done by the calling thread and the order of visitation
1059 /// is not specified. Elements inserted during the execution of `visit`
1060 /// may or may not be visited. The behavior is undefined if hash map
1061 /// manipulators and `getValue*` methods are invoked from within
1062 /// `visitor`, as it may lead to a deadlock. Note that `visitor` can
1063 /// change the value of the visited elements.
1064 int visit(const VisitorFunction& visitor);
1065
1066 /// Serially call the specified `visitor` on each element (if one
1067 /// exists) in this hash map having the specified `key` until every such
1068 /// element has been updated or `visitor` returns `false`. That is, for
1069 /// `(key, value)`, invoke:
1070 /// @code
1071 /// bool visitor(&value, key);
1072 /// @endcode
1073 /// Return the number of elements visited or the negation of that value
1074 /// if visitations stopped because `visitor` returned `false`.
1075 /// `visitor` has exclusive access (i.e., write access) to each element
1076 /// for duration of each invocation. The behavior is undefined if hash
1077 /// map manipulators and `getValue*` methods are invoked from within
1078 /// `visitor`, as it may lead to a deadlock.
1079 int visit(const KEY& key, const VisitorFunction& visitor);
1080
1081 // ACCESSORS
1082
1083 /// Return the index of the bucket, in the array of buckets maintained
1084 /// by this hash map, where elements having the specified `key` are
1085 /// inserted. Note that unless rehash is disabled, the value returned
1086 /// may be obsolete at the time it is returned.
1087 bsl::size_t bucketIndex(const KEY& key) const;
1088
1089 /// Return the number of buckets in the array of buckets maintained by
1090 /// this hash map. Note that unless rehash is disabled, the value
1091 /// returned may be obsolete by the time it is received.
1092 bsl::size_t bucketCount() const;
1093
1094 /// Return the number of elements contained in the bucket at the
1095 /// specified `index` in the array of buckets maintained by this hash
1096 /// map. The behavior is undefined unless
1097 /// `0 <= index < bucketCount()`.
1098 bsl::size_t bucketSize(bsl::size_t index) const;
1099
1100 /// Return `true` if rehash is enabled and rehash is not in progress,
1101 /// and `false` otherwise.
1102 bool canRehash() const;
1103
1104 /// Return `true` if this hash map contains no elements, and `false`
1105 /// otherwise.
1106 bool empty() const;
1107
1108 /// Return (a copy of) the key-equality functor used by this hash map
1109 /// that returns `true` if two `KEY` objects have the same value, and
1110 /// `false` otherwise.
1111 EQUAL equalFunction() const;
1112
1113 /// Load, into the specified `*value`, the value attribute of the first
1114 /// element (of possibly many elements) found in this hash map having
1115 /// the specified `key`. Return 1 on success, and 0 if `key` does not
1116 /// exist in this hash. Note that the return value equals the number of
1117 /// values returned. Also note that, when there are multiple elements
1118 /// having `key`, the selection of "first" is implementation specific
1119 /// and subject to change.
1120 bsl::size_t getValue(VALUE *value, const KEY& key) const;
1121
1122 bsl::size_t getValue(bsl::vector<VALUE> *valuesPtr, const KEY& key) const;
1123 bsl::size_t getValue(std::vector<VALUE> *valuesPtr, const KEY& key) const;
1124#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
1125 bsl::size_t getValue(std::pmr::vector<VALUE> *valuesPtr, const KEY& key)
1126 const;
1127#endif
1128 // Load, into the specified '*valuesPtr', the value attributes of every
1129 // element in this hash map having the specified 'key'. Return the
1130 // number of elements found with 'key'. Note that the order of the
1131 // values returned is not specified.
1132
1133 /// Return (a copy of) the unary hash functor used by this hash map to
1134 /// generate a hash value (of type `std::size_t`) for a `KEY` object.
1135 HASH hashFunction() const;
1136
1137 /// Return `true` if rehash is enabled, or `false` otherwise.
1138 bool isRehashEnabled() const;
1139
1140 /// Return the current quotient of the size of this hash map and the
1141 /// number of buckets. Note that the load factor is a measure of
1142 /// container "fullness"; that is, a high load factor typically implies
1143 /// many collisions (many elements landing in the same bucket) and that
1144 /// decreases performance.
1145 float loadFactor() const;
1146
1147 /// Return the maximum load factor allowed for this hash map. If an
1148 /// insert operation would cause the load factor to exceed the
1149 /// `maxLoadFactor()` and rehashing is enabled, then that insert
1150 /// increases the number of buckets and rehashes the elements of the
1151 /// container into that larger set of buckets.
1152 float maxLoadFactor() const;
1153
1154 /// Return the number of stripes in the hash.
1155 bsl::size_t numStripes() const;
1156
1157 /// Call the specified `visitor` (in an unspecified order) on the
1158 /// elements in this hash table until each element has been visited or
1159 /// `visitor` returns `false`. That is, for `(key, value)`, invoke:
1160 /// @code
1161 /// bool visitor(value, key);
1162 /// @endcode
1163 /// Return the number of elements visited or the negation of that value
1164 /// if visitations stopped because `visitor` returned `false`.
1165 /// `visitor` has read-only access to each element for duration of each
1166 /// invocation. Every element present in this hash map at the time
1167 /// `visit` is invoked will be visited unless it is removed before
1168 /// `visitor` is called for that element. Each visitation is done by
1169 /// the calling thread and the order of visitation is not specified.
1170 /// The behavior is undefined if hash map manipulators are invoked from
1171 /// within `visitor`, as it may lead to a deadlock. Note that `visitor`
1172 /// can *not* change the value of the visited elements.
1173 int visitReadOnly(const ReadOnlyVisitorFunction& visitor) const;
1174
1175 /// Serially call the specified `visitor` on each element (if one
1176 /// exists) in this hash map having the specified `key` until every such
1177 /// element has been visitd or `visitor` returns `false`. That is, for
1178 /// `(key, value)`, invoke:
1179 /// @code
1180 /// bool visitor(value, key);
1181 /// @endcode
1182 /// Return the number of elements visited or the negation of that value
1183 /// if visitations stopped because `visitor` returned `false`.
1184 /// `visitor` has read-only access to each element for duration of each
1185 /// invocation. The behavior is undefined if hash map manipulators are
1186 /// invoked from within `visitor`, as it may lead to a deadlock.
1187 int visitReadOnly(const KEY& key,
1188 const ReadOnlyVisitorFunction& visitor) const;
1189
1190 /// Return the current number of elements in this hash.
1191 bsl::size_t size() const;
1192
1193 // Aspects
1194
1195 /// Return the allocator used by this hash map to supply memory. Note
1196 /// that if no allocator was supplied at construction the default
1197 /// allocator installed at that time is used.
1199};
1200
1201 // ============================================
1202 // class StripedUnorderedContainerImpl_TestUtil
1203 // ============================================
1204
1205/// This class implements a test utility that gives the test driver access
1206/// to the lock / unlock method of the Read/Write mutex. Its purpose is to
1207/// allow testing that the locking actually happens as planned.
1208///
1209/// See @ref bdlcc_stripedunorderedcontainerimpl
1210template <class KEY, class VALUE, class HASH, class EQUAL>
1212
1213 // PRIVATE TYPES
1215
1216 // DATA
1218
1219 public:
1220 // CREATORS
1221
1222 /// Create a `StripedUnorderedContainerImpl_TestUtil` object to test
1223 /// locking in the specified `hash`.
1226
1228 // Destroy this object.
1229
1230 // MANIPULATORS
1231
1232 /// Call the `lockRead` method of `bdlcc::StripedUnorderedContainerImpl`
1233 /// `d_locks_p` lock of the specified `key`.
1234 void lockRead(const KEY& key);
1235
1236 /// Call the `lockWrite` method of
1237 /// `bdlcc::StripedUnorderedContainerImpl` `d_locks_p` lock of the
1238 /// specified `key`.
1239 void lockWrite(const KEY& key);
1240
1241 /// Call the `unlockWrite` method of
1242 /// `bdlcc::StripedUnorderedContainerImpl` `d_locks_p` lock of the
1243 /// specified `key`.
1244 void unlockWrite(const KEY& key);
1245
1246 /// Call the `unlockRead` method of
1247 /// `bdlcc::StripedUnorderedContainerImpl` `d_locks_p` lock of the
1248 /// specified `key`.
1249 void unlockRead(const KEY& key);
1250};
1251
1252 // ===============================================
1253 // class StripedUnorderedContainerImpl_LockElement
1254 // ===============================================
1255
1256/// A mutex + support info; padded to cacheline size, one per stripe
1257///
1258/// See @ref bdlcc_stripedunorderedcontainerimpl
1260
1261 private:
1262 // PRIVATE TYPES
1264
1265 // PRIVATE CONSTANTS
1266 enum {
1267 #if BSLS_PLATFORM_CPU_X86 || BSLS_PLATFORM_CPU_X86_64
1268 k_PREFETCH_ENABLED = 1,
1269 #else
1270 k_PREFETCH_ENABLED = 0,
1271 #endif
1272 // Can be 0 or 1; if prefetch, we use 2 cachelines at a time
1273 k_EFFECTIVE_CACHELINE_SIZE = (1 + k_PREFETCH_ENABLED) *
1275 // Cacheline size to use; may be 1 or 2 cachelines
1276 k_LOCK_PADDING = k_EFFECTIVE_CACHELINE_SIZE >= sizeof(LockType) ?
1277 k_EFFECTIVE_CACHELINE_SIZE - sizeof(LockType) :
1278 2 * k_EFFECTIVE_CACHELINE_SIZE - sizeof(LockType)
1279 };
1280
1281 // DATA
1282 LockType d_lock;
1283 const char d_pad[k_LOCK_PADDING];
1284
1285 public:
1286 // CREATORS
1287
1288 /// Create an empty `StripedUnorderedContainerImpl_LockElement` object.
1290
1291 // MANIPULATORS
1292
1293 /// Read lock the lock element.
1294 void lockR();
1295
1296 /// Write lock the lock element.
1297 void lockW();
1298
1299 /// Read unlock the lock element.
1300 void unlockR();
1301
1302 /// Write unlock the lock element.
1303 void unlockW();
1304};
1305
1306
1307 // ========================================================
1308 // class StripedUnorderedContainerImpl_LockElementReadGuard
1309 // ========================================================
1310
1311/// A guard pattern on StripedUnorderedContainerImpl_LockElement, to release
1312/// on exception, for a lock element locked as read.
1313///
1314/// See @ref bdlcc_stripedunorderedcontainerimpl
1316
1317 private:
1318 // DATA
1319
1320 // Guarded LockElement pointer
1322
1323 public:
1324 // CREATORS
1325
1326 /// Create a guard object
1327 /// `StripedUnorderedContainerImpl_LockElementReadGuard` for the
1328 /// specified `lockElementPtr`
1329 /// `bdlcc::StripedUnorderedContainerImpl_LockElement` object.
1332
1333 /// Release the guarded object
1335
1336 // MANIPULATORS
1337
1338 /// Release the guarded object
1339 void release();
1340};
1341
1342 // =========================================================
1343 // class StripedUnorderedContainerImpl_LockElementWriteGuard
1344 // =========================================================
1345
1346/// A guard pattern on StripedUnorderedContainerImpl_LockElement, to release
1347/// on exception, for a lock element locked as write.
1348///
1349/// See @ref bdlcc_stripedunorderedcontainerimpl
1351
1352 private:
1353 // DATA
1354
1355 // Guarded LockElement pointer
1357
1358 public:
1359 // CREATORS
1360
1361 /// Create a guard object
1362 /// `StripedUnorderedContainerImpl_LockElementWriteGuard` for the
1363 /// specified `lockElementPtr`
1364 /// `bdlcc::StripedUnorderedContainerImpl_LockElement` object.
1367
1368 /// Release the guarded object
1370
1371 // MANIPULATORS
1372
1373 /// Release the guarded object
1374 void release();
1375};
1376
1377/// A vector element needed for efficient sorting for the `insertBulk` and
1378/// `eraseBulk` methods.
1380 public:
1381 // PUBLIC DATA
1384 bsl::size_t d_hashVal;
1385};
1386
1387// FREE OPERATOR
1388
1389/// Return `true` if the specified `lhs` is smaller than the specified `rhs`
1390/// in the order of stripe, and data.
1393
1394// ============================================================================
1395// INLINE DEFINITIONS
1396// ============================================================================
1397
1398 // ----------------------------------------
1399 // class StripedUnorderedContainerImpl_Node
1400 // ----------------------------------------
1401
1402// CREATORS
1403template <class KEY, class VALUE>
1404inline
1407 const KEY& key,
1408 const VALUE& value,
1410 bslma::Allocator *basicAllocator)
1411: d_next_p(nextPtr)
1412, d_allocator_p(bslma::Default::allocator(basicAllocator))
1413{
1415 d_allocator_p,
1416 key);
1417 bslma::DestructorProctor<KEY> proctor(&d_key.object());
1418
1420 d_allocator_p,
1421 value);
1422 proctor.release();
1423}
1424
1425template <class KEY, class VALUE>
1426inline
1429 const KEY& key,
1432 bslma::Allocator *basicAllocator)
1433: d_next_p(nextPtr)
1434, d_allocator_p(bslma::Default::allocator(basicAllocator))
1435{
1437 d_allocator_p,
1438 key);
1439 bslma::DestructorProctor<KEY> proctor(&d_key.object());
1440
1441 VALUE& dummy = value;
1443 dummy,
1444 d_allocator_p);
1445 proctor.release();
1446}
1447
1448template <class KEY, class VALUE>
1451 const KEY& key,
1453 bslma::Allocator *basicAllocator)
1454: d_next_p(nextPtr)
1455, d_allocator_p(bslma::Default::allocator(basicAllocator))
1456{
1458 d_allocator_p,
1459 key);
1460 bslma::DestructorProctor<KEY> proctor(&d_key.object());
1461
1462 bslma::ConstructionUtil::construct(d_value.address(), d_allocator_p);
1463 proctor.release();
1464}
1465
1466
1467template <class KEY, class VALUE>
1468inline
1471{
1472 // Destroy the object buffers content
1473 bslma::DestructionUtil::destroy(d_key.address());
1474 bslma::DestructionUtil::destroy(d_value.address());
1475}
1476
1477
1478// MANIPULATORS
1479template <class KEY, class VALUE>
1480inline
1486
1487template <class KEY, class VALUE>
1488inline
1494
1495template <class KEY, class VALUE>
1496inline
1498{
1499 return d_value.object();
1500}
1501
1502// ACCESSORS
1503template <class KEY, class VALUE>
1504inline
1506{
1507 return d_key.object();
1508}
1509
1510template <class KEY, class VALUE>
1511inline
1514{
1515 return d_next_p;
1516}
1517
1518template <class KEY, class VALUE>
1519inline
1521{
1522 return d_value.object();
1523}
1524
1525 // Aspects
1526
1527template <class KEY, class VALUE>
1528inline
1531{
1532 return d_allocator_p;
1533}
1534
1535 // ------------------------------------------
1536 // class StripedUnorderedContainerImpl_Bucket
1537 // ------------------------------------------
1538
1539// CREATORS
1540template <class KEY, class VALUE>
1541inline
1544 bslma::Allocator *basicAllocator)
1545: d_head_p(NULL)
1546, d_tail_p(NULL)
1547, d_size(0)
1548, d_allocator_p(bslma::Default::allocator(basicAllocator))
1549{
1550}
1551
1552template <class KEY, class VALUE>
1553inline
1557 original,
1559: d_head_p(MoveUtil::move(MoveUtil::access(original).d_head_p))
1560, d_tail_p(MoveUtil::move(MoveUtil::access(original).d_tail_p))
1561, d_size( MoveUtil::access(original).d_size)
1562, d_allocator_p(MoveUtil::access(original).d_allocator_p)
1563{
1564 MoveUtil::access(original).d_head_p = NULL;
1565 MoveUtil::access(original).d_tail_p = NULL;
1566 MoveUtil::access(original).d_size = 0;
1567}
1568
1569template <class KEY, class VALUE>
1570inline
1576
1577// MANIPULATORS
1578template <class KEY, class VALUE>
1579inline
1582{
1583 BSLS_ASSERT(nodePtr->next() == NULL);
1584
1585 if (d_head_p == NULL) {
1586 d_head_p = nodePtr;
1587 }
1588 else {
1589 d_tail_p->setNext(nodePtr);
1590 }
1591 d_tail_p = nodePtr;
1592 ++d_size;
1593}
1594
1595template <class KEY, class VALUE>
1596inline
1598{
1599 // Delete all content in a loop
1600 for (StripedUnorderedContainerImpl_Node<KEY, VALUE> *curNode = d_head_p;
1601 curNode != NULL;) {
1603 curNode->next();
1604 d_allocator_p->deleteObject(curNode);
1605 curNode = nextPtr;
1606 }
1607 d_head_p = NULL;
1608 d_tail_p = NULL;
1609 d_size = 0;
1610}
1611
1612template <class KEY, class VALUE>
1613inline
1619
1620template <class KEY, class VALUE>
1621inline
1623 int amount)
1624{
1625 d_size += amount;
1626}
1627
1628template <class KEY, class VALUE>
1629inline
1635
1636template <class KEY, class VALUE>
1637inline
1639 bsl::size_t value)
1640{
1641 d_size = value;
1642}
1643
1644template <class KEY, class VALUE>
1645inline
1651
1652template <class KEY, class VALUE>
1653template <class EQUAL>
1655 const KEY& key,
1656 const EQUAL& equal,
1657 const VALUE& value,
1658 BucketScope scope)
1659{
1660 if (d_head_p == NULL) {
1661 d_head_p = new (*d_allocator_p)
1663 key,
1664 value,
1665 NULL,
1666 d_allocator_p);
1667 d_tail_p = d_head_p;
1668 d_size = 1;
1669 return 0; // RETURN
1670 }
1671
1673 int count = 0;
1674 for (; curNode != NULL; curNode = curNode->next()) {
1675 if (equal(curNode->key(), key)) {
1676 curNode->value() = value;
1677 if (e_BUCKETSCOPE_FIRST == scope) {
1678 return 1; // RETURN
1679 }
1680 ++count;
1681 }
1682 }
1683 if (count > 0) {
1684 return count; // RETURN
1685 }
1688 key,
1689 value,
1690 NULL,
1691 d_allocator_p);
1692 d_tail_p->setNext(newNode);
1693 d_tail_p = d_tail_p->next();
1694 ++d_size;
1695 return 0;
1696}
1697
1698template <class KEY, class VALUE>
1699template <class EQUAL>
1701 const KEY& key,
1702 const EQUAL& equal,
1704{
1705 if (d_head_p == NULL) {
1706 d_head_p = new (*d_allocator_p)
1708 key,
1710 NULL,
1711 d_allocator_p);
1712 d_tail_p = d_head_p;
1713 d_size = 1;
1714 return 0; // RETURN
1715 }
1717 for (; curNode != NULL; curNode = curNode->next()) {
1718 if (equal(curNode->key(), key)) {
1719#if defined(BSLMF_MOVABLEREF_USES_RVALUE_REFERENCES)
1720 curNode->value() = bslmf::MovableRefUtil::move(value);
1721#else
1722 curNode->value() = value;
1723#endif
1724 return 1; // RETURN
1725 }
1726 }
1729 key,
1731 NULL,
1732 d_allocator_p);
1733 d_tail_p->setNext(newNode);
1734 d_tail_p = d_tail_p->next();
1735 ++d_size;
1736 return 0;
1737}
1738
1739// ACCESSORS
1740template <class KEY, class VALUE>
1741inline
1743{
1744 return d_size == 0;
1745}
1746
1747template <class KEY, class VALUE>
1748inline
1751{
1752 return d_head_p;
1753}
1754
1755template <class KEY, class VALUE>
1756inline
1758{
1759 return d_size;
1760}
1761
1762template <class KEY, class VALUE>
1763inline
1766{
1767 return d_tail_p;
1768}
1769
1770 // Aspects
1771
1772template <class KEY, class VALUE>
1773inline
1775 const
1776{
1777 return d_allocator_p;
1778}
1779
1780 // -----------------------------------------------
1781 // class StripedUnorderedContainerImpl_LockElement
1782 // -----------------------------------------------
1783
1784// CREATORS
1785inline
1792
1793// MANIPULATORS
1794inline
1799
1800inline
1805
1806inline
1811
1812inline
1817
1818 // --------------------------------------------------------
1819 // class StripedUnorderedContainerImpl_LockElementReadGuard
1820 // --------------------------------------------------------
1821
1822// CREATORS
1823inline
1830
1831// MANIPULATORS
1832inline
1838
1839inline
1841{
1842 if (d_lockElement_p) {
1843 d_lockElement_p->unlockR();
1844 d_lockElement_p = NULL;
1845 }
1846}
1847
1848 // ---------------------------------------------------------
1849 // class StripedUnorderedContainerImpl_LockElementWriteGuard
1850 // ---------------------------------------------------------
1851
1852// CREATORS
1853inline
1860
1861// MANIPULATORS
1862inline
1868
1869inline
1871{
1872 if (d_lockElement_p) {
1873 d_lockElement_p->unlockW();
1874 d_lockElement_p = NULL;
1875 }
1876}
1877
1878 // -----------------------------------
1879 // class StripedUnorderedContainerImpl
1880 // -----------------------------------
1881
1882// PRIVATE CLASS METHODS
1883template <class KEY, class VALUE, class HASH, class EQUAL>
1884inline
1885bsl::size_t
1887 bsl::size_t numBuckets,
1888 bsl::size_t numStripes)
1889{
1890 // 'numBuckets' must not less than 2, and must be a power of 2. To avoid
1891 // unused stripes, we also require numBuckets >= numStripes.
1892 if (numBuckets < 2) {
1893 numBuckets = 2;
1894 }
1895 if (numBuckets < numStripes) {
1896 numBuckets = numStripes;
1897 }
1898 numBuckets = powerCeil(numBuckets);
1899 return numBuckets;
1900}
1901
1902template <class KEY, class VALUE, class HASH, class EQUAL>
1903inline
1904bool StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::alwaysTrue(
1905 const VALUE&)
1906{
1907 return true;
1908}
1909
1910template <class KEY, class VALUE, class HASH, class EQUAL>
1911inline
1912bsl::size_t StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::powerCeil(
1913 bsl::size_t num)
1914{
1915 if (num <= 1) {
1916 return 1; // RETURN
1917 }
1918 int power = 2;
1919 --num;
1920 while (num >>= 1) {
1921 power <<= 1;
1922 }
1923 return power;
1924}
1925
1926// PRIVATE MANIPULATORS
1927template <class KEY, class VALUE, class HASH, class EQUAL>
1928inline
1929void StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::checkRehash()
1930{
1931 float loadF = loadFactor();
1932 if (d_maxLoadFactor < loadF && canRehash()) {
1933 int ratio = static_cast<int>(loadF / d_maxLoadFactor);
1934 int growthFactor = 2;
1935 while (growthFactor < ratio) {
1936 growthFactor <<= 1;
1937 }
1938 bsl::size_t newNumBuckets = d_numBuckets * growthFactor;
1939 rehash(newNumBuckets);
1940 }
1941}
1942
1943template <class KEY, class VALUE, class HASH, class EQUAL>
1944inline
1945bsl::size_t StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::erase(
1946 const KEY& key,
1947 Scope scope)
1948{
1949 return eraseIf(key, scope, alwaysTrue);
1950}
1951
1952template <class KEY, class VALUE, class HASH, class EQUAL>
1953template <class RANDOM_ITER>
1954bsl::size_t StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::eraseBulk(
1955 RANDOM_ITER first,
1956 RANDOM_ITER last,
1957 Scope scope)
1958{
1959 BSLS_ASSERT(first <= last);
1960
1961 bool eraseAll = scope == e_SCOPE_ALL;
1962 bsl::size_t count = 0;
1963 // For each key, store in a vector its stripe, location, and hash value.
1964 int dataSize = static_cast<int>(last - first);
1965
1967 sortIdxs(dataSize, bslma::Default::defaultAllocator());
1968 for (int i = 0; i < dataSize; ++i) {
1969 sortIdxs[i].d_hashVal = d_hasher(first[i]);
1970 bsl::size_t bucketIdx =
1972 d_numBuckets);
1973 sortIdxs[i].d_stripeIdx = static_cast<int>(bucketToStripe(bucketIdx));
1974 sortIdxs[i].d_dataIdx = i;
1975 }
1976 // Sort it by stripe, and location
1977 bsl::sort(sortIdxs.begin(), sortIdxs.end());
1978
1979 // Lock each stripe, and process all data points in it. Do not recalculate
1980 // hash code (hence keeping the hash value).
1981 int curStripeIdx;
1982 for (int j = 0; j < dataSize;) {
1983 curStripeIdx = sortIdxs[j].d_stripeIdx;
1984 LockElement& lockElement = d_locks_p[curStripeIdx];
1985 lockElement.lockW();
1986 LEWGuard guard(&lockElement);
1987 for (; j < dataSize && sortIdxs[j].d_stripeIdx == curStripeIdx; ++j) {
1988 int dataIdx = sortIdxs[j].d_dataIdx;
1989 bsl::size_t bucketIdx =
1991 sortIdxs[j].d_hashVal,
1992 d_numBuckets);
1993
1994 StripedUnorderedContainerImpl_Bucket<KEY, VALUE> &bucket =
1995 d_buckets[bucketIdx];
1996
1997 const KEY& key = first[dataIdx];
1998
1999 Node **prevNodeAddress = bucket.headAddress();
2000 Node *prevNode = NULL;
2001 while (*prevNodeAddress) {
2002 Node *node = *prevNodeAddress;
2003 if (d_comparator(node->key(), key)) {
2004 *prevNodeAddress = node->next();
2005 if (bucket.tail() == node) {
2006 bucket.setTail(prevNode);
2007 }
2008 d_allocator_p->deleteObject(node);
2009 bucket.incrementSize(-1);
2010 d_numElements.addRelaxed(-1);
2011 ++count;
2012 if (!eraseAll) {
2013 break;
2014 }
2015 }
2016 else {
2017 prevNode = node;
2018 prevNodeAddress = node->nextAddress();
2019 }
2020 }
2021 }
2022 }
2023
2024 return count;
2025}
2026
2027template <class KEY, class VALUE, class HASH, class EQUAL>
2028bsl::size_t StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::eraseIf(
2029 const KEY& key,
2030 Scope scope,
2031 const EraseIfValuePredicate& predicate)
2032{
2033 bool eraseAll = scope == e_SCOPE_ALL;
2034 bsl::size_t bucketIdx;
2035 LEWGuard guard(lockWrite(&bucketIdx, key));
2036
2037 StripedUnorderedContainerImpl_Bucket<KEY, VALUE> &bucket =
2038 d_buckets[bucketIdx];
2039
2040 typedef StripedUnorderedContainerImpl_Node<KEY, VALUE> Node;
2041
2042 bsl::size_t count = 0;
2043
2044 Node **prevNodeAddress = bucket.headAddress();
2045 Node *prevNode = NULL;
2046 while (*prevNodeAddress) {
2047 Node *node = *prevNodeAddress;
2048 if (d_comparator(node->key(), key) && predicate(node->value())) {
2049 *prevNodeAddress = node->next();
2050 if (bucket.tail() == node) {
2051 bucket.setTail(prevNode);
2052 }
2053 d_allocator_p->deleteObject(node);
2054 bucket.incrementSize(-1);
2055 d_numElements.addRelaxed(-1);
2056 ++count;
2057 if (!eraseAll) {
2058 return count; // RETURN
2059 }
2060 }
2061 else {
2062 prevNode = node;
2063 prevNodeAddress = node->nextAddress();
2064 }
2065 }
2066 return count;
2067}
2068
2069template <class KEY, class VALUE, class HASH, class EQUAL>
2070inline
2071bsl::size_t StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::insert(
2072 const KEY& key,
2073 const VALUE& value,
2074 Multiplicity multiplicity)
2075{
2076 bool insertAlways = multiplicity == e_INSERT_ALWAYS;
2077
2078 bsl::size_t bucketIdx;
2079 LEWGuard guard(lockWrite(&bucketIdx, key));
2080
2081 bsl::size_t ret = 0;
2082 if (insertAlways) {
2083 // Insert, ignoring an existing value if any. Use only in multimap.
2084 Node *node = new (*d_allocator_p)
2085 StripedUnorderedContainerImpl_Node<KEY, VALUE>(key,
2086 value,
2087 NULL,
2088 d_allocator_p);
2089 d_buckets[bucketIdx].addNode(node);
2090 }
2091 else {
2092 // Update only the first value if key exists. Use only in hash map.
2093 ret = d_buckets[bucketIdx].setValue(
2094 key,
2095 d_comparator,
2096 value,
2098 }
2099 if (ret == 1) {
2100 return 0; // RETURN
2101 }
2102 guard.release();
2103 d_numElements.addRelaxed(1);
2104 checkRehash();
2105 return 1;
2106}
2107
2108template <class KEY, class VALUE, class HASH, class EQUAL>
2109inline
2110bsl::size_t StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::insert(
2111 const KEY& key,
2113 Multiplicity multiplicity)
2114{
2115 bool insertAlways = multiplicity == e_INSERT_ALWAYS;
2116
2117 bsl::size_t bucketIdx;
2118 LEWGuard guard(lockWrite(&bucketIdx, key));
2119
2120 bsl::size_t ret = 0;
2121 if (insertAlways) {
2122 // Insert, ignoring an existing value if any. Use only in multimap.
2123 Node *node = new (*d_allocator_p)
2124 Node(key, bslmf::MovableRefUtil::move(value), NULL, d_allocator_p);
2125 d_buckets[bucketIdx].addNode(node);
2126 }
2127 else {
2128 // Update only the first value if key exists. Use only in hash map.
2129 ret = d_buckets[bucketIdx].setValue(
2130 key,
2131 d_comparator,
2133 }
2134 if (ret == 1) {
2135 return 0; // RETURN
2136 }
2137 guard.release();
2138 d_numElements.addRelaxed(1);
2139 checkRehash();
2140 return 1;
2141}
2142
2143template <class KEY, class VALUE, class HASH, class EQUAL>
2144template <class RANDOM_ITER>
2145bsl::size_t StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::insertBulk(
2146 RANDOM_ITER first,
2147 RANDOM_ITER last,
2148 Multiplicity multiplicity)
2149{
2150 BSLS_ASSERT(first <= last);
2151
2152 bool insertAlways = multiplicity == e_INSERT_ALWAYS;
2153 bsl::size_t count = 0;
2154 int dataSize = static_cast<int>(last - first);
2155
2157 sortIdxs(dataSize, bslma::Default::defaultAllocator());
2158
2159 // For each key, store in a vector its stripe, location, and hash value.
2160 for (int i = 0; i < dataSize; ++i) {
2161 sortIdxs[i].d_hashVal = d_hasher(first[i].first);
2162 bsl::size_t bucketIdx =
2164 d_numBuckets);
2165 sortIdxs[i].d_stripeIdx = static_cast<int>(bucketToStripe(bucketIdx));
2166 sortIdxs[i].d_dataIdx = i;
2167 }
2168 // Sort it by stripe, and location
2169 bsl::sort(sortIdxs.begin(), sortIdxs.end());
2170
2171 // Lock each stripe, and process all data points in it. Do not recalculate
2172 // hash code (hence keeping the hash value).
2173 int curStripeIdx;
2174 for (int j = 0; j < dataSize;) {
2175 curStripeIdx = sortIdxs[j].d_stripeIdx;
2176 LockElement& lockElement = d_locks_p[curStripeIdx];
2177 lockElement.lockW();
2178 LEWGuard guard(&lockElement);
2179 for (; j < dataSize && sortIdxs[j].d_stripeIdx == curStripeIdx; ++j) {
2180 int dataIdx = sortIdxs[j].d_dataIdx;
2181 bsl::size_t bucketIdx =
2183 sortIdxs[j].d_hashVal,
2184 d_numBuckets);
2185 const KEY& key = first[dataIdx].first;
2186 const VALUE& value = first[dataIdx].second;
2187
2188 if (insertAlways) {
2189 // Insert, ignoring an existing value if any. Use only in
2190 // multimap.
2191 StripedUnorderedContainerImpl_Node<KEY, VALUE> *node =
2192 new (*d_allocator_p)
2193 StripedUnorderedContainerImpl_Node<KEY, VALUE>(
2194 key,
2195 value,
2196 NULL,
2197 d_allocator_p);
2198 d_buckets[bucketIdx].addNode(node);
2199 ++count;
2200 d_numElements.addRelaxed(1);
2201 } else {
2202 bsl::size_t ret = d_buckets[bucketIdx].setValue(
2203 key,
2204 d_comparator,
2205 value,
2206 StripedUnorderedContainerImpl_Bucket<KEY, VALUE>::
2207 e_BUCKETSCOPE_FIRST);
2208 if (ret == 0) {
2209 ++count;
2210 d_numElements.addRelaxed(1);
2211 }
2212 }
2213 }
2214 }
2215 checkRehash();
2216 return count;
2217}
2218
2219template <class KEY, class VALUE, class HASH, class EQUAL>
2220int StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::setComputedValue(
2221 const KEY& key,
2222 const VisitorFunction& visitor,
2223 Scope scope)
2224{
2225 typedef StripedUnorderedContainerImpl_Bucket<KEY, VALUE> BucketClass;
2226 typename BucketClass::BucketScope setAll = scope == e_SCOPE_ALL
2227 ? BucketClass::e_BUCKETSCOPE_ALL
2228 : BucketClass::e_BUCKETSCOPE_FIRST;
2229
2230 bsl::size_t bucketIdx;
2231 LEWGuard guard(lockWrite(&bucketIdx, key));
2232
2233 StripedUnorderedContainerImpl_Bucket<KEY, VALUE>& bucket =
2234 d_buckets[bucketIdx];
2235 // Loop on the elements in the list
2236 int count = 0;
2237 StripedUnorderedContainerImpl_Node<KEY, VALUE> *curNode = bucket.head();
2238 for (; curNode != NULL; curNode = curNode->next()) {
2239 if (d_comparator(curNode->key(), key)) {
2240 bool ret = visitor(&curNode->value(), key);
2241 if (false == setAll) {
2242 return ret ? 1 : -1; // RETURN
2243 }
2244 ++count;
2245 if (false == ret) {
2246 return -count; // RETURN
2247 }
2248 }
2249 }
2250 if (count > 0) {
2251 return count; // RETURN
2252 }
2253
2254 // Not found - process as false, and return 0.
2255 StripedUnorderedContainerImpl_Node<KEY, VALUE> *addNode =
2256 new (*d_allocator_p)
2257 StripedUnorderedContainerImpl_Node<KEY, VALUE>(key,
2258 NULL,
2259 d_allocator_p);
2260 {
2262 StripedUnorderedContainerImpl_Node<KEY, VALUE>,
2264 proctor(addNode, d_allocator_p);
2265
2266 visitor(&addNode->value(), key);
2267 proctor.release();
2268 }
2269
2270 if (bucket.head() == NULL) {
2271 bucket.setHead(addNode);
2272 bucket.setTail(addNode);
2273 }
2274 else {
2275 bucket.tail()->setNext(addNode);
2276 bucket.setTail(addNode);
2277 }
2278 d_numElements.addRelaxed(1);
2279 bucket.incrementSize(1);
2280 guard.release();
2281 checkRehash();
2282 return 0;
2283}
2284
2285template <class KEY, class VALUE, class HASH, class EQUAL>
2286inline
2287bsl::size_t StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::setValue(
2288 const KEY& key,
2289 const VALUE& value,
2290 Scope scope)
2291{
2292 typedef StripedUnorderedContainerImpl_Bucket<KEY, VALUE> BucketClass;
2293 typename BucketClass::BucketScope setAll = scope == e_SCOPE_ALL
2294 ? BucketClass::e_BUCKETSCOPE_ALL
2295 : BucketClass::e_BUCKETSCOPE_FIRST;
2296
2297 bsl::size_t bucketIdx;
2298 LEWGuard guard(lockWrite(&bucketIdx, key));
2299
2300 StripedUnorderedContainerImpl_Bucket<KEY, VALUE>& bucket =
2301 d_buckets[bucketIdx];
2302
2303 bsl::size_t count = bucket.setValue(key, d_comparator, value, setAll);
2304 if (count == 0) {
2305 guard.release();
2306 d_numElements.addRelaxed(1);
2307 checkRehash();
2308 }
2309 return count;
2310}
2311
2312// PRIVATE ACCESSORS
2313template <class KEY, class VALUE, class HASH, class EQUAL>
2314inline
2315bsl::size_t
2316StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::bucketIndex(
2317 const KEY& key,
2318 bsl::size_t numBuckets) const
2319{
2320 bsl::size_t hashVal = d_hasher(key);
2321 bsl::size_t bucketIdx =
2323 return bucketIdx;
2324}
2325
2326template <class KEY, class VALUE, class HASH, class EQUAL>
2327inline
2328bsl::size_t
2329StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::bucketToStripe(
2330 bsl::size_t bucketIndex) const
2331{
2332 return bucketIndex & d_hashMask;
2333}
2334
2335template <class KEY, class VALUE, class HASH, class EQUAL>
2336template <class VECTOR>
2337inline
2338bsl::size_t
2339StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::getValueImpl(
2340 VECTOR *valuesPtr,
2341 const KEY& key) const
2342{
2343 static const bool isVector =
2344 bsl::is_same<bsl::vector<VALUE>, VECTOR>::value
2345#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
2346 || bsl::is_same<std::pmr::vector<VALUE>, VECTOR>::value
2347#endif
2348 || bsl::is_same<std::vector<VALUE>, VECTOR>::value;
2349 BSLMF_ASSERT(isVector);
2350
2351 BSLS_ASSERT(NULL != valuesPtr);
2352
2353 valuesPtr->clear();
2354
2355 bsl::size_t bucketIdx;
2356 LERGuard guard(lockRead(&bucketIdx, key));
2357
2358 bsl::size_t count = 0;
2359 const StripedUnorderedContainerImpl_Bucket<KEY, VALUE>& bucket = d_buckets[
2360 bucketIdx];
2361 // Loop on the elements in the list
2362 StripedUnorderedContainerImpl_Node<KEY, VALUE> *curNode = bucket.head();
2363 for (; curNode != NULL; curNode = curNode->next()) {
2364 if (d_comparator(curNode->key(), key)) {
2365 valuesPtr->push_back(curNode->value());
2366 ++count;
2367 }
2368 }
2369 return count;
2370}
2371
2372template <class KEY, class VALUE, class HASH, class EQUAL>
2373inline
2374StripedUnorderedContainerImpl_LockElement *
2375StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::lockRead(
2376 bsl::size_t *bucketIdx,
2377 const KEY& key) const
2378{
2379 // From key, get hash value, and current number of buckets.
2380 bsl::size_t hashVal = d_hasher(key);
2381 bsl::size_t numBuckets = d_numBuckets;
2382 bsl::size_t bucketIndex =
2383 bslalg::HashTableImpUtil::computeBucketIndex(hashVal, d_numBuckets);
2384 bsl::size_t stripeIdx = bucketToStripe(bucketIndex);
2385 LockElement& lockElement = d_locks_p[stripeIdx];
2386 lockElement.lockR();
2387 // When we get the lock, did the number of buckets change?
2388 if (numBuckets != d_numBuckets) {
2389 bucketIndex =
2390 bslalg::HashTableImpUtil::computeBucketIndex(hashVal, d_numBuckets);
2391 }
2392 *bucketIdx = bucketIndex;
2393 return &lockElement;
2394}
2395
2396template <class KEY, class VALUE, class HASH, class EQUAL>
2397inline
2398StripedUnorderedContainerImpl_LockElement *
2399StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL>::lockWrite(
2400 bsl::size_t *bucketIdx,
2401 const KEY& key) const
2402{
2403 // From key, get hash value, and current number of buckets.
2404 bsl::size_t hashVal = d_hasher(key);
2405 bsl::size_t numBuckets = d_numBuckets;
2406 bsl::size_t bucketIndex =
2407 bslalg::HashTableImpUtil::computeBucketIndex(hashVal, d_numBuckets);
2408 bsl::size_t stripeIdx = bucketToStripe(bucketIndex);
2409 LockElement& lockElement = d_locks_p[stripeIdx];
2410 lockElement.lockW();
2411
2412 // When we get the lock, did the number of buckets change?
2413 if (numBuckets != d_numBuckets) {
2414 bucketIndex =
2415 bslalg::HashTableImpUtil::computeBucketIndex(hashVal, d_numBuckets);
2416 }
2417 *bucketIdx = bucketIndex;
2418 return &lockElement;
2419}
2420
2421// CREATORS
2422template <class KEY, class VALUE, class HASH, class EQUAL>
2423inline
2426 bsl::size_t numInitialBuckets,
2427 bsl::size_t numStripes,
2428 bslma::Allocator *basicAllocator)
2429: d_numStripes(powerCeil(numStripes))
2430, d_numBuckets(adjustBuckets(numInitialBuckets, d_numStripes))
2431, d_hashMask(d_numStripes - 1)
2432, d_maxLoadFactor(1.0)
2433, d_hasher()
2434, d_comparator()
2435, d_statePad()
2436, d_numElementsPad()
2437, d_buckets(d_numBuckets, basicAllocator)
2438, d_allocator_p(bslma::Default::allocator(basicAllocator))
2439{
2440 d_state = k_REHASH_ENABLED; // Rehash enabled, not in progress
2441 d_numElements = 0; // Hash empty
2442
2443 // Allocate array of 'LockElement' objects, and construct them.
2444 d_locks_p = reinterpret_cast<LockElement*>(
2445 d_allocator_p->allocate(d_numStripes * sizeof(LockElement)));
2446 for (bsl::size_t i = 0; i < d_numStripes; ++i) {
2447 bslma::ConstructionUtil::construct(&d_locks_p[i], d_allocator_p);
2448 }
2449}
2450
2451template <class KEY, class VALUE, class HASH, class EQUAL>
2452inline
2455{
2456 for (bsl::size_t i = 0; i < d_numStripes; ++i) {
2457 bslma::DestructionUtil::destroy(&d_locks_p[i]);
2458 }
2459 d_allocator_p->deallocate(d_locks_p);
2460}
2461
2462// MANIPULATORS
2463template <class KEY, class VALUE, class HASH, class EQUAL>
2464inline
2466{
2467 // Locking all stripes will inherently block until a rehash will complete
2468 for (bsl::size_t i = 0; i < d_numStripes; ++i) {
2469 d_locks_p[i].lockW();
2470 }
2471 for (bsl::size_t j = 0; j < d_numBuckets; ++j) {
2472 d_buckets[j].clear();
2473 }
2474 d_numElements = 0;
2475 for (bsl::size_t i = 0; i < d_numStripes; ++i) {
2476 d_locks_p[i].unlockW();
2477 }
2478}
2479
2480template <class KEY, class VALUE, class HASH, class EQUAL>
2481inline
2483{
2484 for (;;) {
2485 int oldState = d_state.load();
2486 int newState = oldState & ~k_REHASH_ENABLED;
2487 if (oldState == d_state.testAndSwap(oldState, newState)) {
2488 return; // RETURN
2489 }
2490 }
2491}
2492
2493template <class KEY, class VALUE, class HASH, class EQUAL>
2494inline
2496{
2497 for (;;) {
2498 int oldState = d_state.load();
2499 int newState = oldState | k_REHASH_ENABLED;
2500 if (oldState == d_state.testAndSwap(oldState, newState)) {
2501 break;
2502 }
2503 }
2504}
2505
2506template <class KEY, class VALUE, class HASH, class EQUAL>
2507inline
2509 const KEY& key)
2510{
2511 return erase(key, e_SCOPE_ALL);
2512}
2513
2514template <class KEY, class VALUE, class HASH, class EQUAL>
2515inline
2517 const KEY& key,
2518 const EraseIfValuePredicate& predicate)
2519{
2520 return eraseIf(key, e_SCOPE_ALL, predicate);
2521}
2522
2523template <class KEY, class VALUE, class HASH, class EQUAL>
2524template <class RANDOM_ITER>
2525inline
2526bsl::size_t
2528 RANDOM_ITER first,
2529 RANDOM_ITER last)
2530{
2531 BSLS_ASSERT(first <= last);
2532
2533 return eraseBulk(first, last, e_SCOPE_ALL);
2534}
2535
2536template <class KEY, class VALUE, class HASH, class EQUAL>
2537template <class RANDOM_ITER>
2538inline
2539bsl::size_t
2541 RANDOM_ITER first,
2542 RANDOM_ITER last)
2543{
2544 BSLS_ASSERT(first <= last);
2545
2546 return eraseBulk(first, last, e_SCOPE_FIRST);
2547}
2548
2549template <class KEY, class VALUE, class HASH, class EQUAL>
2550inline
2552 const KEY& key)
2553{
2554 return erase(key, e_SCOPE_FIRST);
2555}
2556
2557template <class KEY, class VALUE, class HASH, class EQUAL>
2558inline
2559bsl::size_t
2561 const KEY& key,
2562 const EraseIfValuePredicate& predicate)
2563{
2564 return eraseIf(key, e_SCOPE_FIRST, predicate);
2565}
2566
2567template <class KEY, class VALUE, class HASH, class EQUAL>
2568inline
2570 const KEY& key,
2571 const VALUE& value)
2572{
2573 insert(key, value, e_INSERT_ALWAYS);
2574}
2575
2576template <class KEY, class VALUE, class HASH, class EQUAL>
2577inline
2579 const KEY& key,
2581{
2582 insert(key, bslmf::MovableRefUtil::move(value), e_INSERT_ALWAYS);
2583}
2584
2585template <class KEY, class VALUE, class HASH, class EQUAL>
2586template <class RANDOM_ITER>
2587inline
2589 RANDOM_ITER first,
2590 RANDOM_ITER last)
2591{
2592 BSLS_ASSERT(first <= last);
2593
2594 insertBulk(first, last, e_INSERT_ALWAYS);
2595}
2596
2597template <class KEY, class VALUE, class HASH, class EQUAL>
2598template <class RANDOM_ITER>
2599inline
2600bsl::size_t
2602 RANDOM_ITER first,
2603 RANDOM_ITER last)
2604{
2605 BSLS_ASSERT(first <= last);
2606
2607 return insertBulk(first, last, e_INSERT_UNIQUE);
2608}
2609
2610template <class KEY, class VALUE, class HASH, class EQUAL>
2611inline
2612bsl::size_t
2614 const KEY& key,
2615 const VALUE& value)
2616{
2617 return insert(key, value, e_INSERT_UNIQUE);
2618}
2619
2620template <class KEY, class VALUE, class HASH, class EQUAL>
2621inline
2622bsl::size_t
2624 const KEY& key,
2626{
2627 return insert(key, bslmf::MovableRefUtil::move(value), e_INSERT_UNIQUE);
2628}
2629
2630template <class KEY, class VALUE, class HASH, class EQUAL>
2631inline
2633 float newMaxLoadFactor)
2634{
2635 BSLS_ASSERT(0 < newMaxLoadFactor);
2636
2637 d_maxLoadFactor = newMaxLoadFactor;
2638 checkRehash();
2639}
2640
2641template <class KEY, class VALUE, class HASH, class EQUAL>
2643 bsl::size_t numBuckets)
2644{
2645 // 'numBuckets' must not less than 2, and must be a power of 2. To avoid
2646 // unused stripes, we also require numBuckets >= numStripes.
2647 if (numBuckets < 2) {
2648 numBuckets = 2;
2649 }
2650 if (numBuckets < d_numStripes) {
2651 numBuckets = d_numStripes;
2652 }
2653 numBuckets = powerCeil(numBuckets);
2654
2655 if (numBuckets == d_numBuckets) { // Skip if no change in # of buckets
2656 return; // RETURN
2657 }
2658 if (!canRehash()) { // Skip if can't rehash
2659 return; // RETURN
2660 }
2661
2662 // Set state to rehash
2663 int oldState = d_state.testAndSwap(
2664 k_REHASH_ENABLED,
2665 k_REHASH_ENABLED | k_REHASH_IN_PROGRESS);
2666 if (oldState != k_REHASH_ENABLED) { // State changed under our feet
2667 return; // RETURN
2668 }
2669
2670 // Allocate a new data vector
2672 numBuckets,
2673 d_allocator_p);
2674
2675 // Main loop on stripes: lock a stripe and process all buckets in it
2676 for (bsl::size_t i = 0; i < d_numStripes; ++i) {
2677 d_locks_p[i].lockW();
2678 // Loop on the buckets of the current stripe. This is simple, as the
2679 // stripe is the last bits in a bucket index. We start with the
2680 // current stripe as the first bucket, and add 'd_numStripes' for the
2681 // next bucket, until 'd_numBuckets'.
2682 for (bsl::size_t j = i; j < d_numBuckets; j += d_numStripes) {
2684 d_buckets[j];
2685 // Process the nodes in the bucket. Note that we do not need to
2686 // delete the old node and allocate a new one, but can simply move
2687 // it.
2689 bucket.head(); curNode != NULL;) {
2691 curNode->next();
2692
2693 bsl::size_t newBucketIdx = bucketIndex(curNode->key(),
2694 numBuckets);
2695 curNode->setNext(NULL);
2696 newBuckets[newBucketIdx].addNode(curNode);
2697 curNode = nextPtr;
2698 }
2699 bucket.setHead(NULL);
2700 bucket.setTail(NULL);
2701 bucket.setSize(0);
2702 }
2703 }
2704 // Swap 'newBuckets' and 'd_buckets'. This requires the same allocator.
2705 d_buckets.swap(newBuckets);
2706
2707 // Update number of buckets.
2708 d_numBuckets = numBuckets;
2709
2710 // Unlock all stripes. This could not be done on the spot, as we store the
2711 /// rehashed data in a new set of buckets.
2712 for (bsl::size_t i = 0; i < d_numStripes; ++i) {
2713 d_locks_p[i].unlockW();
2714 }
2715
2716 // Rehash no longer in progress
2717 d_state = d_state & ~k_REHASH_IN_PROGRESS;
2718}
2719
2720template <class KEY, class VALUE, class HASH, class EQUAL>
2721inline
2722int
2724 const KEY& key,
2725 const VisitorFunction& visitor)
2726{
2727 return setComputedValue(key, visitor, e_SCOPE_ALL);
2728}
2729
2730template <class KEY, class VALUE, class HASH, class EQUAL>
2731inline
2732int
2734 const KEY& key,
2735 const VisitorFunction& visitor)
2736{
2737 return setComputedValue(key, visitor, e_SCOPE_FIRST);
2738}
2739
2740template <class KEY, class VALUE, class HASH, class EQUAL>
2741inline
2742bsl::size_t
2744 const KEY& key,
2745 const VALUE& value)
2746{
2747 return setValue(key, value, e_SCOPE_ALL);
2748}
2749
2750template <class KEY, class VALUE, class HASH, class EQUAL>
2751inline
2752bsl::size_t
2754 const KEY& key,
2755 const VALUE& value)
2756{
2757 return setValue(key, value, e_SCOPE_FIRST);
2758}
2759
2760template <class KEY, class VALUE, class HASH, class EQUAL>
2761inline
2762bsl::size_t
2764 const KEY& key,
2766{
2767 bsl::size_t bucketIdx;
2768 LEWGuard guard(lockWrite(&bucketIdx, key));
2769
2771 d_buckets[bucketIdx];
2772
2773 bsl::size_t count = bucket.setValue(key,
2774 d_comparator,
2776 if (count == 0) {
2777 guard.release();
2778 d_numElements.addRelaxed(1);
2779 checkRehash();
2780 }
2781 return count;
2782}
2783
2784template <class KEY, class VALUE, class HASH, class EQUAL>
2785inline
2787 const KEY& key,
2788 const VisitorFunction& visitor)
2789{
2790 return visit(key, visitor);
2791}
2792
2793template <class KEY, class VALUE, class HASH, class EQUAL>
2795 const VisitorFunction& visitor)
2796{
2797 // Main loop on stripes: lock a stripe and process all buckets in it
2798 int count = 0;
2799 for (bsl::size_t i = 0; i < d_numStripes; ++i) {
2800 d_locks_p[i].lockW();
2801 // Loop on the buckets of the current stripe. This is simple, as the
2802 // stripe is the last bits in a bucket index. We start with the
2803 // current stripe as the first bucket, and add 'd_numStripes' for the
2804 // next bucket, until 'd_numBuckets'.
2805 for (bsl::size_t j = i; j < d_numBuckets; j += d_numStripes) {
2807 d_buckets[j];
2808 // Loop on the nodes in the bucket.
2810 bucket.head(); curNode != NULL;
2811 curNode = curNode->next()) {
2812 ++count;
2813 bool ret = visitor(&(curNode->value()), curNode->key());
2814 if (!ret) {
2815 d_locks_p[i].unlockW();
2816 return -count; // RETURN
2817 }
2818 }
2819 }
2820 d_locks_p[i].unlockW();
2821 }
2822 return count;
2823}
2824
2825template <class KEY, class VALUE, class HASH, class EQUAL>
2826inline
2828 const KEY& key,
2829 const VisitorFunction& visitor)
2830{
2831 bsl::size_t bucketIdx;
2832 LEWGuard guard(lockWrite(&bucketIdx, key));
2833
2835 d_buckets[bucketIdx];
2836
2837 // Loop on the elements in the list
2838 int count = 0;
2840 for (; curNode != NULL; curNode = curNode->next()) {
2841 if (d_comparator(curNode->key(), key)) {
2842 ++count;
2843 bool ret = visitor(&curNode->value(), key);
2844 if (ret == false) {
2845 return -count; // RETURN
2846 }
2847 }
2848 }
2849 return count;
2850}
2851
2852// ACCESSORS
2853template <class KEY, class VALUE, class HASH, class EQUAL>
2854inline
2855bsl::size_t
2860
2861template <class KEY, class VALUE, class HASH, class EQUAL>
2862inline
2863bsl::size_t
2865 const KEY& key) const
2866{
2867 return bucketIndex(key, d_numBuckets);
2868}
2869
2870template <class KEY, class VALUE, class HASH, class EQUAL>
2871inline
2873 bsl::size_t index) const
2874{
2875 BSLS_ASSERT(index < bucketCount());
2876
2877 return d_buckets[index].size();
2878}
2879
2880template <class KEY, class VALUE, class HASH, class EQUAL>
2881inline
2883{
2884 return d_state == k_REHASH_ENABLED;
2885}
2886
2887template <class KEY, class VALUE, class HASH, class EQUAL>
2888inline
2890{
2891 for (bsl::size_t i = 0; i < d_numBuckets; ++i) {
2892 if (!d_buckets[i].empty()) {
2893 return false; // RETURN
2894 }
2895 }
2896 return true;
2897}
2898
2899template <class KEY, class VALUE, class HASH, class EQUAL>
2900inline
2901EQUAL
2906
2907template <class KEY, class VALUE, class HASH, class EQUAL>
2908inline
2910 VALUE *value,
2911 const KEY& key) const
2912{
2913 BSLS_ASSERT(NULL != value);
2914
2915 bsl::size_t bucketIdx;
2916 LERGuard guard(lockRead(&bucketIdx, key));
2917
2918 const StripedUnorderedContainerImpl_Bucket<KEY, VALUE>& bucket = d_buckets[
2919 bucketIdx];
2920 // Loop on the elements in the list
2922 for (; curNode != NULL; curNode = curNode->next()) {
2923 if (d_comparator(curNode->key(), key)) {
2924 *value = curNode->value();
2925 return 1; // RETURN
2926 }
2927 }
2928 return 0;
2929}
2930
2931template <class KEY, class VALUE, class HASH, class EQUAL>
2932inline
2934 bsl::vector<VALUE> *valuesPtr,
2935 const KEY& key) const
2936{
2937 return getValueImpl(valuesPtr, key);
2938}
2939
2940template <class KEY, class VALUE, class HASH, class EQUAL>
2941inline
2943 std::vector<VALUE> *valuesPtr,
2944 const KEY& key) const
2945{
2946 return getValueImpl(valuesPtr, key);
2947}
2948
2949#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
2950template <class KEY, class VALUE, class HASH, class EQUAL>
2951inline
2953 std::pmr::vector<VALUE> *valuesPtr,
2954 const KEY& key) const
2955{
2956 return getValueImpl(valuesPtr, key);
2957}
2958#endif
2959
2960template <class KEY, class VALUE, class HASH, class EQUAL>
2961inline
2962HASH
2967
2968template <class KEY, class VALUE, class HASH, class EQUAL>
2969inline
2970bool
2972{
2973 return d_state & k_REHASH_ENABLED;
2974}
2975
2976template <class KEY, class VALUE, class HASH, class EQUAL>
2977inline
2978float
2980{
2981 return static_cast<float>(d_numElements.loadRelaxed()) /
2982 static_cast<float>(d_numBuckets);
2983}
2984
2985template <class KEY, class VALUE, class HASH, class EQUAL>
2986inline
2987float
2989{
2990 return d_maxLoadFactor;
2991}
2992
2993template <class KEY, class VALUE, class HASH, class EQUAL>
2994inline
2995bsl::size_t
2997{
2998 return static_cast<bsl::size_t>(d_numStripes);
2999}
3000
3001template <class KEY, class VALUE, class HASH, class EQUAL>
3003 const ReadOnlyVisitorFunction& visitor) const
3004{
3005 // Main loop on stripes: lock a stripe and process all buckets in it
3006 int count = 0;
3007 for (bsl::size_t i = 0; i < d_numStripes; ++i) {
3008 d_locks_p[i].lockR();
3009 // Loop on the buckets of the current stripe. This is simple, as the
3010 // stripe is the last bits in a bucket index. We start with the
3011 // current stripe as the first bucket, and add 'd_numStripes' for the
3012 // next bucket, until 'd_numBuckets'.
3013 for (bsl::size_t j = i; j < d_numBuckets; j += d_numStripes) {
3015 d_buckets[j];
3016 // Loop on the nodes in the bucket.
3018 bucket.head(); curNode != NULL;
3019 curNode = curNode->next()) {
3020 ++count;
3021 bool ret = visitor(curNode->value(), curNode->key());
3022 if (!ret) {
3023 d_locks_p[i].unlockR();
3024 return -count; // RETURN
3025 }
3026 }
3027 }
3028 d_locks_p[i].unlockR();
3029 }
3030 return count;
3031}
3032
3033template <class KEY, class VALUE, class HASH, class EQUAL>
3034inline
3036 const KEY& key,
3037 const ReadOnlyVisitorFunction& visitor) const
3038{
3039 bsl::size_t bucketIdx;
3040 LERGuard guard(lockRead(&bucketIdx, key));
3041
3043 d_buckets[bucketIdx];
3044
3045 // Loop on the elements in the list
3046 int count = 0;
3048 for (; curNode != NULL; curNode = curNode->next()) {
3049 if (d_comparator(curNode->key(), key)) {
3050 ++count;
3051 bool ret = visitor(curNode->value(), key);
3052 if (ret == false) {
3053 return -count; // RETURN
3054 }
3055 }
3056 }
3057 return count;
3058}
3059
3060template <class KEY, class VALUE, class HASH, class EQUAL>
3061inline
3062bsl::size_t
3064{
3065 return d_numElements.loadRelaxed();
3066}
3067
3068 // Aspects
3069
3070template <class KEY, class VALUE, class HASH, class EQUAL>
3071inline
3074{
3075 return d_allocator_p;
3076}
3077
3078 // --------------------------------------------
3079 // class StripedUnorderedContainerImpl_TestUtil
3080 // --------------------------------------------
3081
3082// CREATORS
3083template <class KEY, class VALUE, class HASH, class EQUAL>
3084inline
3091
3092// MANIPULATORS
3093template <class KEY, class VALUE, class HASH, class EQUAL>
3094inline
3096 const KEY& key)
3097{
3098 bsl::size_t bucketIdx = d_hash.bucketIndex(key);
3099 bsl::size_t stripeIdx = d_hash.bucketToStripe(bucketIdx);
3100 LockElement& lockElement = d_hash.d_locks_p[stripeIdx];
3101 lockElement.lockR();
3102}
3103
3104template <class KEY, class VALUE, class HASH, class EQUAL>
3105inline
3107 lockWrite(const KEY& key)
3108{
3109 bsl::size_t bucketIdx = d_hash.bucketIndex(key);
3110 bsl::size_t stripeIdx = d_hash.bucketToStripe(bucketIdx);
3111 LockElement& lockElement = d_hash.d_locks_p[stripeIdx];
3112 lockElement.lockW();
3113}
3114
3115template <class KEY, class VALUE, class HASH, class EQUAL>
3116inline
3118 unlockRead(const KEY& key)
3119{
3120 bsl::size_t bucketIdx = d_hash.bucketIndex(key);
3121 bsl::size_t stripeIdx = d_hash.bucketToStripe(bucketIdx);
3122 LockElement& lockElement = d_hash.d_locks_p[stripeIdx];
3123 lockElement.unlockR();
3124}
3125
3126template <class KEY, class VALUE, class HASH, class EQUAL>
3127inline
3129 unlockWrite(const KEY& key)
3130{
3131 bsl::size_t bucketIdx = d_hash.bucketIndex(key);
3132 bsl::size_t stripeIdx = d_hash.bucketToStripe(bucketIdx);
3133 LockElement& lockElement = d_hash.d_locks_p[stripeIdx];
3134 lockElement.unlockW();
3135}
3136
3137} // close package namespace
3138
3139// FREE OPERATORS
3140inline
3141bool bdlcc::operator<(const StripedUnorderedContainerImpl_SortItem& lhs,
3142 const StripedUnorderedContainerImpl_SortItem& rhs)
3143{
3144 if (lhs.d_stripeIdx < rhs.d_stripeIdx) {
3145 return true; // RETURN
3146 }
3147 if (lhs.d_stripeIdx > rhs.d_stripeIdx) {
3148 return false; // RETURN
3149 }
3150 if (lhs.d_dataIdx < rhs.d_dataIdx) {
3151 return true; // RETURN
3152 }
3153 return false;
3154}
3155
3156namespace bslma {
3157
3158template <class KEY, class VALUE, class HASH, class EQUAL>
3159struct UsesBslmaAllocator<bdlcc::StripedUnorderedContainerImpl<KEY,
3160 VALUE,
3161 HASH,
3162 EQUAL> >
3163 : bsl::true_type {
3164};
3165
3166} // close namespace bslma
3167
3168
3169
3170#endif
3171
3172// ----------------------------------------------------------------------------
3173// Copyright 2020 Bloomberg Finance L.P.
3174//
3175// Licensed under the Apache License, Version 2.0 (the "License"); you may not
3176// use this file except in compliance with the License. You may obtain a copy
3177// of the License at
3178//
3179// http://www.apache.org/licenses/LICENSE-2.0
3180//
3181// Unless required by applicable law or agreed to in writing, software
3182// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
3183// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
3184// License for the specific language governing permissions and limitations
3185// under the License.
3186// ----------------------------- END-OF-FILE ----------------------------------
3187
3188/** @} */
3189/** @} */
3190/** @} */
Definition bdlcc_stripedunorderedcontainerimpl.h:304
void addNode(StripedUnorderedContainerImpl_Node< KEY, VALUE > *nodePtr)
Add the specified nodePtr node at the end of this bucket.
Definition bdlcc_stripedunorderedcontainerimpl.h:1580
StripedUnorderedContainerImpl_Node< KEY, VALUE > * tail() const
Return address of the tail (node) of this bucket list.
Definition bdlcc_stripedunorderedcontainerimpl.h:1765
void setSize(bsl::size_t value)
Set the size attribute of this bucket to the specified value.
Definition bdlcc_stripedunorderedcontainerimpl.h:1638
void setHead(StripedUnorderedContainerImpl_Node< KEY, VALUE > *value)
Definition bdlcc_stripedunorderedcontainerimpl.h:1630
BucketScope
Definition bdlcc_stripedunorderedcontainerimpl.h:338
@ e_BUCKETSCOPE_FIRST
Definition bdlcc_stripedunorderedcontainerimpl.h:340
@ e_BUCKETSCOPE_ALL
Definition bdlcc_stripedunorderedcontainerimpl.h:341
~StripedUnorderedContainerImpl_Bucket()
Destroy this object.
Definition bdlcc_stripedunorderedcontainerimpl.h:1572
void setTail(StripedUnorderedContainerImpl_Node< KEY, VALUE > *value)
Definition bdlcc_stripedunorderedcontainerimpl.h:1646
BSLMF_NESTED_TRAIT_DECLARATION(StripedUnorderedContainerImpl_Bucket, bslma::UsesBslmaAllocator)
bsl::size_t size() const
Return the current number of elements in this bucket.
Definition bdlcc_stripedunorderedcontainerimpl.h:1757
StripedUnorderedContainerImpl_Node< KEY, VALUE > ** headAddress()
Return the address of the head (node) of this bucket list.
Definition bdlcc_stripedunorderedcontainerimpl.h:1615
void incrementSize(int amount)
Definition bdlcc_stripedunorderedcontainerimpl.h:1622
StripedUnorderedContainerImpl_Node< KEY, VALUE > * head() const
Return the head (node) of this bucket list.
Definition bdlcc_stripedunorderedcontainerimpl.h:1750
bslma::Allocator * allocator() const
Definition bdlcc_stripedunorderedcontainerimpl.h:1774
bsl::size_t setValue(const KEY &key, const EQUAL &equal, const VALUE &value, BucketScope scope)
Definition bdlcc_stripedunorderedcontainerimpl.h:1654
bool empty() const
Definition bdlcc_stripedunorderedcontainerimpl.h:1742
void clear()
Empty StripedUnorderedContainerImpl_Bucket and delete all nodes.
Definition bdlcc_stripedunorderedcontainerimpl.h:1597
Definition bdlcc_stripedunorderedcontainerimpl.h:1315
StripedUnorderedContainerImpl_LockElementReadGuard(StripedUnorderedContainerImpl_LockElement *lockElementPtr)
Definition bdlcc_stripedunorderedcontainerimpl.h:1825
~StripedUnorderedContainerImpl_LockElementReadGuard()
Release the guarded object.
Definition bdlcc_stripedunorderedcontainerimpl.h:1834
void release()
Release the guarded object.
Definition bdlcc_stripedunorderedcontainerimpl.h:1840
Definition bdlcc_stripedunorderedcontainerimpl.h:1350
~StripedUnorderedContainerImpl_LockElementWriteGuard()
Release the guarded object.
Definition bdlcc_stripedunorderedcontainerimpl.h:1864
StripedUnorderedContainerImpl_LockElementWriteGuard(StripedUnorderedContainerImpl_LockElement *lockElementPtr)
Definition bdlcc_stripedunorderedcontainerimpl.h:1855
void release()
Release the guarded object.
Definition bdlcc_stripedunorderedcontainerimpl.h:1870
Definition bdlcc_stripedunorderedcontainerimpl.h:1259
void lockR()
Read lock the lock element.
Definition bdlcc_stripedunorderedcontainerimpl.h:1795
void unlockW()
Write unlock the lock element.
Definition bdlcc_stripedunorderedcontainerimpl.h:1813
void unlockR()
Read unlock the lock element.
Definition bdlcc_stripedunorderedcontainerimpl.h:1807
StripedUnorderedContainerImpl_LockElement()
Create an empty StripedUnorderedContainerImpl_LockElement object.
Definition bdlcc_stripedunorderedcontainerimpl.h:1787
void lockW()
Write lock the lock element.
Definition bdlcc_stripedunorderedcontainerimpl.h:1801
Definition bdlcc_stripedunorderedcontainerimpl.h:206
~StripedUnorderedContainerImpl_Node()
Destroy this object.
Definition bdlcc_stripedunorderedcontainerimpl.h:1470
bslma::Allocator * allocator() const
Definition bdlcc_stripedunorderedcontainerimpl.h:1530
const KEY & key() const
Return a const reference to the key attribute of this object.
Definition bdlcc_stripedunorderedcontainerimpl.h:1505
void setNext(StripedUnorderedContainerImpl_Node *nextPtr)
Set this node's pointer-to-next-node to the specified nextPtr.
Definition bdlcc_stripedunorderedcontainerimpl.h:1489
StripedUnorderedContainerImpl_Node ** nextAddress()
Return the address of the pointer to the next node.
Definition bdlcc_stripedunorderedcontainerimpl.h:1482
StripedUnorderedContainerImpl_Node * next() const
Return the pointer to the next node.
Definition bdlcc_stripedunorderedcontainerimpl.h:1513
VALUE & value()
Definition bdlcc_stripedunorderedcontainerimpl.h:1497
Definition bdlcc_stripedunorderedcontainerimpl.h:1211
void unlockRead(const KEY &key)
Definition bdlcc_stripedunorderedcontainerimpl.h:3118
void lockWrite(const KEY &key)
Definition bdlcc_stripedunorderedcontainerimpl.h:3107
void lockRead(const KEY &key)
Definition bdlcc_stripedunorderedcontainerimpl.h:3095
void unlockWrite(const KEY &key)
Definition bdlcc_stripedunorderedcontainerimpl.h:3129
StripedUnorderedContainerImpl_TestUtil(StripedUnorderedContainerImpl< KEY, VALUE, HASH, EQUAL > &hash)
Definition bdlcc_stripedunorderedcontainerimpl.h:3086
Definition bdlcc_stripedunorderedcontainerimpl.h:473
bsl::size_t getValue(VALUE *value, const KEY &key) const
Definition bdlcc_stripedunorderedcontainerimpl.h:2909
bsl::size_t bucketSize(bsl::size_t index) const
Definition bdlcc_stripedunorderedcontainerimpl.h:2872
bool empty() const
Definition bdlcc_stripedunorderedcontainerimpl.h:2889
StripedUnorderedContainerImpl_Node< KEY, VALUE > Node
Node in a bucket.
Definition bdlcc_stripedunorderedcontainerimpl.h:483
float loadFactor() const
Definition bdlcc_stripedunorderedcontainerimpl.h:2979
bsl::size_t setValueFirst(const KEY &key, bslmf::MovableRef< VALUE > value)
Definition bdlcc_stripedunorderedcontainerimpl.h:2763
int update(const KEY &key, const VisitorFunction &visitor)
Definition bdlcc_stripedunorderedcontainerimpl.h:2786
int visitReadOnly(const KEY &key, const ReadOnlyVisitorFunction &visitor) const
Definition bdlcc_stripedunorderedcontainerimpl.h:3035
float maxLoadFactor() const
Definition bdlcc_stripedunorderedcontainerimpl.h:2988
bsl::size_t insertUnique(const KEY &key, bslmf::MovableRef< VALUE > value)
Definition bdlcc_stripedunorderedcontainerimpl.h:2623
bsl::size_t setValueFirst(const KEY &key, const VALUE &value)
Definition bdlcc_stripedunorderedcontainerimpl.h:2753
bsl::size_t numStripes() const
Return the number of stripes in the hash.
Definition bdlcc_stripedunorderedcontainerimpl.h:2996
HASH hashFunction() const
Definition bdlcc_stripedunorderedcontainerimpl.h:2963
bsl::function< bool(VALUE *, const KEY &)> VisitorFunction
Definition bdlcc_stripedunorderedcontainerimpl.h:497
bsl::size_t bucketIndex(const KEY &key) const
Definition bdlcc_stripedunorderedcontainerimpl.h:2864
bsl::size_t insertBulkUnique(RANDOM_ITER first, RANDOM_ITER last)
Definition bdlcc_stripedunorderedcontainerimpl.h:2601
void rehash(bsl::size_t numBuckets)
Definition bdlcc_stripedunorderedcontainerimpl.h:2642
bsl::size_t insertUnique(const KEY &key, const VALUE &value)
Definition bdlcc_stripedunorderedcontainerimpl.h:2613
bsl::pair< KEY, VALUE > KVType
Value type of a bulk insert entry.
Definition bdlcc_stripedunorderedcontainerimpl.h:486
int visit(const KEY &key, const VisitorFunction &visitor)
Definition bdlcc_stripedunorderedcontainerimpl.h:2827
void disableRehash()
Prevent rehash until the enableRehash method is called.
Definition bdlcc_stripedunorderedcontainerimpl.h:2482
bool isRehashEnabled() const
Return true if rehash is enabled, or false otherwise.
Definition bdlcc_stripedunorderedcontainerimpl.h:2971
bsl::size_t eraseFirstIf(const KEY &key, const EraseIfValuePredicate &predicate)
Definition bdlcc_stripedunorderedcontainerimpl.h:2560
StripedUnorderedContainerImpl(bsl::size_t numInitialBuckets=k_DEFAULT_NUM_BUCKETS, bsl::size_t numStripes=k_DEFAULT_NUM_STRIPES, bslma::Allocator *basicAllocator=0)
Definition bdlcc_stripedunorderedcontainerimpl.h:2425
void clear()
Definition bdlcc_stripedunorderedcontainerimpl.h:2465
void insertBulkAlways(RANDOM_ITER first, RANDOM_ITER last)
Definition bdlcc_stripedunorderedcontainerimpl.h:2588
bsl::size_t size() const
Return the current number of elements in this hash.
Definition bdlcc_stripedunorderedcontainerimpl.h:3063
bsl::size_t eraseFirst(const KEY &key)
Definition bdlcc_stripedunorderedcontainerimpl.h:2551
bsl::size_t eraseBulkFirst(RANDOM_ITER first, RANDOM_ITER last)
Definition bdlcc_stripedunorderedcontainerimpl.h:2540
void insertAlways(const KEY &key, bslmf::MovableRef< VALUE > value)
Definition bdlcc_stripedunorderedcontainerimpl.h:2578
~StripedUnorderedContainerImpl()
Destroy this hash map. This method is not thread-safe.
Definition bdlcc_stripedunorderedcontainerimpl.h:2454
bsl::size_t setValueAll(const KEY &key, const VALUE &value)
Definition bdlcc_stripedunorderedcontainerimpl.h:2743
void insertAlways(const KEY &key, const VALUE &value)
Definition bdlcc_stripedunorderedcontainerimpl.h:2569
bslma::Allocator * allocator() const
Definition bdlcc_stripedunorderedcontainerimpl.h:3073
void maxLoadFactor(float newMaxLoadFactor)
Definition bdlcc_stripedunorderedcontainerimpl.h:2632
int visitReadOnly(const ReadOnlyVisitorFunction &visitor) const
Definition bdlcc_stripedunorderedcontainerimpl.h:3002
int setComputedValueAll(const KEY &key, const VisitorFunction &visitor)
Definition bdlcc_stripedunorderedcontainerimpl.h:2723
bsl::function< bool(const VALUE &)> EraseIfValuePredicate
Definition bdlcc_stripedunorderedcontainerimpl.h:519
void enableRehash()
Definition bdlcc_stripedunorderedcontainerimpl.h:2495
bool canRehash() const
Definition bdlcc_stripedunorderedcontainerimpl.h:2882
bsl::size_t eraseAllIf(const KEY &key, const EraseIfValuePredicate &predicate)
Definition bdlcc_stripedunorderedcontainerimpl.h:2516
friend class StripedUnorderedContainerImpl_LockElement
Definition bdlcc_stripedunorderedcontainerimpl.h:614
bsl::size_t eraseBulkAll(RANDOM_ITER first, RANDOM_ITER last)
Definition bdlcc_stripedunorderedcontainerimpl.h:2527
int setComputedValueFirst(const KEY &key, const VisitorFunction &visitor)
Definition bdlcc_stripedunorderedcontainerimpl.h:2733
bsl::function< bool(const VALUE &, const KEY &)> ReadOnlyVisitorFunction
Definition bdlcc_stripedunorderedcontainerimpl.h:510
bsl::size_t bucketCount() const
Definition bdlcc_stripedunorderedcontainerimpl.h:2856
bsl::size_t getValue(std::vector< VALUE > *valuesPtr, const KEY &key) const
Definition bdlcc_stripedunorderedcontainerimpl.h:2942
@ k_DEFAULT_NUM_STRIPES
Definition bdlcc_stripedunorderedcontainerimpl.h:479
@ k_DEFAULT_NUM_BUCKETS
Definition bdlcc_stripedunorderedcontainerimpl.h:478
EQUAL equalFunction() const
Definition bdlcc_stripedunorderedcontainerimpl.h:2902
bsl::size_t getValue(bsl::vector< VALUE > *valuesPtr, const KEY &key) const
Definition bdlcc_stripedunorderedcontainerimpl.h:2933
int visit(const VisitorFunction &visitor)
Definition bdlcc_stripedunorderedcontainerimpl.h:2794
bsl::size_t eraseAll(const KEY &key)
Definition bdlcc_stripedunorderedcontainerimpl.h:2508
Forward declaration.
Definition bslstl_function.h:934
Definition bslstl_pair.h:1210
Definition bslstl_vector.h:1025
Definition bslma_allocator.h:457
virtual void * allocate(size_type size)=0
Definition bslma_destructorproctor.h:259
void release()
Definition bslma_destructorproctor.h:325
Definition bslma_rawdeleterproctor.h:242
Definition bslmf_movableref.h:751
Definition bslmt_readerwritermutex.h:244
void unlockRead()
Definition bslmt_readerwritermutex.h:373
void unlockWrite()
Definition bslmt_readerwritermutex.h:379
void lockRead()
Definition bslmt_readerwritermutex.h:343
void lockWrite()
Definition bslmt_readerwritermutex.h:349
Definition bsls_atomic.h:743
#define BSLMF_ASSERT(expr)
Definition bslmf_assert.h:229
#define BSLS_ASSERT(X)
Definition bsls_assert.h:1804
#define BSLS_IDENT(str)
Definition bsls_ident.h:195
Definition bdlcc_boundedqueue.h:270
bool operator<(const StripedUnorderedContainerImpl_SortItem &lhs, const StripedUnorderedContainerImpl_SortItem &rhs)
Definition balxml_encoderoptions.h:68
Definition bdlcc_stripedunorderedcontainerimpl.h:1379
int d_stripeIdx
Definition bdlcc_stripedunorderedcontainerimpl.h:1382
int d_dataIdx
Definition bdlcc_stripedunorderedcontainerimpl.h:1383
bsl::size_t d_hashVal
Definition bdlcc_stripedunorderedcontainerimpl.h:1384
Definition bslstl_equalto.h:311
Definition bslstl_hash.h:498
Definition bslmf_issame.h:146
static std::size_t computeBucketIndex(std::size_t hashCode, std::size_t numBuckets)
Definition bslalg_hashtableimputil.h:845
static void moveConstruct(TARGET_TYPE *address, TARGET_TYPE &original, bslma::Allocator *allocator)
Definition bslalg_scalarprimitives.h:1642
static void construct(TARGET_TYPE *address, const ALLOCATOR &allocator)
Definition bslma_constructionutil.h:1243
static Allocator * defaultAllocator()
Definition bslma_default.h:889
Definition bslma_usesbslmaallocator.h:343
static MovableRef< t_TYPE > move(t_TYPE &reference) BSLS_KEYWORD_NOEXCEPT
Definition bslmf_movableref.h:1060
@ e_CACHE_LINE_SIZE
Definition bslmt_platform.h:213
Definition bsls_objectbuffer.h:276
TYPE * address()
Definition bsls_objectbuffer.h:334
TYPE & object()
Definition bsls_objectbuffer.h:351