// bdlcc_stripedunorderedmultimap.h                                   -*-C++-*-
#ifndef INCLUDED_BDLCC_STRIPEDUNORDEREDMULTIMAP
#define INCLUDED_BDLCC_STRIPEDUNORDEREDMULTIMAP

#include <bsls_ident.h>
BSLS_IDENT("$Id: $")

//@PURPOSE: Provide a bucket-group locking (*striped*) unordered multimap.
//
//@CLASSES:
//  bdlcc::StripedUnorderedMultiMap: Striped hash multimap
//
//@SEE_ALSO: bdlcc_stripedunorderedmap, bdlcc_stripedunorderedimpl
//
//@DESCRIPTION: This component provides a single concurrent (fully thread-safe)
// associative container, 'bdlcc::StripedUnorderedMultiMap', that partitions
// the underlying hash table into a (user defined) number of "bucket groups"
// and controls access to each bucket group by a separate read-write lock.
// This design allows greater concurrency (and improved performance) than a
// 'bsl::unordered_multimap' object protected by a single lock.
//
// 'bdlcc::StripedUnorderedMultiMap' differs from 'bdlcc::StripedUnorderedMap'
// in that the former allows multiple elements to have the same key value but
// the later requires that each element have a unique key value.  Methods of
// the two classes have similar names and semantics differing only where the
// different key policy pertains.
//
// The terms "bucket", "load factor", and "rehash" have the same meaning as
// they do in the 'bslstl_unorderedmultimap' component (see
// {'bslstl_unorderedmultimap'|Unordered Multimap Configuration}).  A general
// introduction to these ideas can be found at:
// https://en.wikipedia.org/wiki/Hash_table
//
// 'bdlcc::StripedUnorderedMultiMap' (and concurrent containers in general)
// does not provide iterators that allow users to manipulate or traverse the
// values of elements in a map.  Alternatively, this container provides the
// 'setComputedValue*' methods that allows users to change the value for a
// given key via a user provided functor and the 'visit' method that will apply
// a user provided functor the value of every key in the map.
//
// The 'bdlcc::StripedUnorderedMultiMap' class is an *irregular* value-semantic
// type, even if 'KEY' and 'VALUE' are VSTs.  This class does not implement
// equality comparison, assignment operator, or copy constructor.
//
///Thread Safety
///-------------
// The 'bdlcc::StripedUnorderedMultiMap' class template is fully thread-safe
// (see {'bsldoc_glossary'|Fully Thread-Safe}), assuming that the allocator is
// fully thread-safe.  Each method is executed by the calling thread.
//
///Runtime Complexity
///------------------
//..
//  +----------------------------------------------------+--------------------+
//  | Operation                                          | Complexity         |
//  +====================================================+====================+
//  | insert, setValueFirst, setValueAll,                | Average: O[1]      |
//  | setComputedValueAll,  setComputedValueFirst, update| Worst:   O[n]      |
//  +----------------------------------------------------+--------------------+
//  | eraseFirst, eraseAll, getValueFirst, getValueAll   | Average: O[1]      |
//  |                                                    | Worst:   O[n]      |
//  +----------------------------------------------------+--------------------+
//  | visit(key, visitor)                                | Average: O[1]      |
//  | visitReadOnly(key, visitor)                        | Worst:   O[n]      |
//  +----------------------------------------------------+--------------------+
//  | insertBulk, k elements                             | Average: O[k]      |
//  |                                                    | Worst:   O[n*k]    |
//  +----------------------------------------------------+--------------------+
//  | examine                                            | Average: O[1]      |
//  |                                                    | Worst:   O[n]      |
//  +----------------------------------------------------+--------------------+
//  | eraseBulkAll, k elements                           | Average: O[k]      |
//  |                                                    | Worst:   O[n*k]    |
//  +----------------------------------------------------+--------------------+
//  | rehash                                             | O[n]               |
//  +----------------------------------------------------+--------------------+
//  | visit(visitor), visitReadOnly(visitor)             | O[n]               |
//  +----------------------------------------------------+--------------------+
//..
//
///Number of Stripes
///-----------------
// Performance improves monotonically when the number of stripes increases.
// However, the rate of improvement decreases, and reaches a plateau.  The
// plateau is reached roughly at four times the number of the threads
// *concurrently* using the hash map.
//
///Set vs. Insert Methods
///----------------------
// This container provides several 'set*' methods and similarly named 'insert*'
// methods that have nearly identical semantics.  Both update the value of an
// existing element and both add a new element if the element sought is not
// present.  Conceptually, the emphasis of the 'set*' methods is the former, so
// its return value is the number of elements updated, and the intent of
// 'insert*' methods is to add elements, so its return value is the number of
// new elements.
//
///Rehash
///------
//
///Concurrent Rehash
///- - - - - - - - -
// A rehash operation is a re-organization of the hash map to a different
// number of buckets.  This is a heavy operation that interferes with, but does
// *not* disallow, other operations on the container.  Rehash is warranted when
// the current load factor exceeds the current maximum allowed load factor.
// Expressed explicitly:
//..
//  bucketCount() <= maxLoadFactor() * size();
//..
// This above condition is tested implicitly by several methods and if found
// true (and if rehash is enabled and rehash is not underway), a rehash is
// started.  The methods that check the load factor are:
//
//: o All methods that insert elements (i.e., increase 'size()').
//: o The 'maxLoadFactor(newMaxLoadFactor)' method.
//: o The 'rehash' method.
//
///Rehash Control
/// - - - - - - -
// 'enableRehash' and 'disableRehash' methods are provided to control the
// rehash enable flag.  Note that disabling rehash does not impact a rehash in
// progress.
//
///Usage
///-----
// In this section we show intended use of this component.
//
///Example 1: Basic Usage
/// - - - - - - - - - - -
// This example shows some basic usage of 'bdlcc::StripedUnorderedMultiMap'.
//
// First, we define a 'bdlcc::StripedUnorderedMultiMap' object, 'myFriends',
// that maps 'int' to 'bsl::string':
//..
//  bdlcc::StripedUnorderedMultiMap<int, bsl::string> myFriends;
//..
// Notice that we are using the default value number of buckets, number of
// stripes, and allocator.
//
// Then, we insert three elements into the map and verify that the size is the
// expected value:
//..
//  assert(0 == myFriends.size());
//  myFriends.insert(0, "Alex");
//  myFriends.insert(1, "John");
//  myFriends.insert(2, "Rob");
//  assert(3 == myFriends.size());
//..
// Next, we demonstrate 'insertBulk' by creating a vector of three key-value
// pairs and add them to the map using a single method call:
//..
//  typedef bsl::pair<int, bsl::string> PairType;
//  bsl::vector<PairType> insertData;
//  insertData.push_back(PairType(3, "Jim"));
//  insertData.push_back(PairType(4, "Jeff"));
//  insertData.push_back(PairType(5, "Ian" ));
//  assert(3 == insertData.size())
//
//  assert(3 == myFriends.size());
//  myFriends.insertBulk(insertData.begin(), insertData.end());
//  assert(6 == myFriends.size());
//..
// Then, we use 'getValueFirst' method to retrieve the previously inserted
// string associated with the value 1:
//..
//  bsl::string value;
//  bsl::size_t rc = myFriends.getValueFirst(&value, 1);
//  assert(1      == rc);
//  assert("John" == value);
//..
// Now, we insert two additional elements, each having key values that already
// appear in the hash map:
//..
//  myFriends.insert(3, "Steve");
//  assert(7 == myFriends.size());
//
//  myFriends.insert(4, "Tim");
//  assert(8 == myFriends.size());
//..
// Finally, we use the 'getValueAll' method to retrieve both values associated
// with the key 3:
//..
//  bsl::vector<bsl::string> values;
//  rc = myFriends.getValueAll(&values, 3);
//  assert(2 == rc);
//
//  assert(2            == values.size());
//  assert(values.end() != bsl::find(values.begin(), values.end(), "Jim"));
//  assert(values.end() != bsl::find(values.begin(), values.end(), "Steve"));
//..
// Notice that the results have the expected number and values.  Also notice
// that we must search the results for the expected values because the order in
// which values are retrieved is not specified.

#include <bdlscm_version.h>

#include <bdlcc_stripedunorderedcontainerimpl.h>

#include <bslmf_movableref.h>

#include <bsls_assert.h>
#include <bsls_libraryfeatures.h>

#include <bsl_functional.h>

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
# include <memory_resource>
#endif

#include <vector>

namespace BloombergLP {
namespace bdlcc {

                    // ==============================
                    // class StripedUnorderedMultiMap
                    // ==============================

template <class KEY,
          class VALUE,
          class HASH  = bsl::hash<KEY>,
          class EQUAL = bsl::equal_to<KEY> >
class StripedUnorderedMultiMap {
    // This class template defines a fully thread-safe container that provides
    // a mapping from keys (of template parameter type 'KEY') to their
    // associated mapped values (of template parameter type 'VALUE').
    //
    // The buckets of this hash map are guarded by 'numStripes' reader-writer
    // locks, a value specified on construction.  Partitioning the buckets
    // among several locks allows greater overall concurrency than a
    // 'bsl::unordered_multimap' object guarded by a single lock.
    //
    // The interface is inspired by, but not identical to that of
    // 'bsl::unordered_multimap'.  Notably absent are iterators, which are of
    // limited practicality in the typical use case because they are readily
    // invalidated when the map population is open to modification by multiple
    // threads.

  private:
    // PRIVATE TYPES
    typedef StripedUnorderedContainerImpl<KEY, VALUE, HASH, EQUAL> Impl;

    // DATA
    Impl d_imp;
        // implementation of the striped hash map

    // NOT IMPLEMENTED
    StripedUnorderedMultiMap(
                     const StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>&);
                                                                    // = delete
    StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>&
           operator=(const StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>&);
                                                                    // = delete

  public:
    // PUBLIC CONSTANTS
    enum {
        k_DEFAULT_NUM_BUCKETS  = 16, // Default number of buckets
        k_DEFAULT_NUM_STRIPES  =  4  // Default number of stripes
    };

    // PUBLIC TYPES
    typedef bsl::pair<KEY, VALUE> KVType;
        // Value type of a bulk insert entry.

    typedef bsl::function<bool (VALUE *, const KEY&)> VisitorFunction;
        // An alias to a function meeting the following contract:
        //..
        //  bool visitorFunction(VALUE *value, const KEY& key);
        //      // Visit the specified 'value' attribute associated with the
        //      // specified 'key'.  Return 'true' if this function may be
        //      // called on additional elements, and 'false' otherwise (i.e.,
        //      // if no other elements should be visited).  Note that this
        //      // functor can change the value associated with 'key'.
        //..

    typedef bsl::function<bool (const VALUE&, const KEY&)>
                                                       ReadOnlyVisitorFunction;
        // An alias to a function meeting the following contract:
        //..
        //  bool readOnlyVisitorFunction(const VALUE& value, const KEY& key);
        //      // Visit the specified 'value' attribute associated with the
        //      // specified 'key'.  Return 'true' if this function may be
        //      // called on additional elements, and 'false' otherwise (i.e.,
        //      // if no other elements should be visited).  Note that this
        //      // functor can *not* change the values associated with 'key'
        //      // and 'value'.
        //..

    // CREATORS
    explicit StripedUnorderedMultiMap(
                   bsl::size_t       numInitialBuckets = k_DEFAULT_NUM_BUCKETS,
                   bsl::size_t       numStripes        = k_DEFAULT_NUM_STRIPES,
                   bslma::Allocator *basicAllocator = 0);
    explicit StripedUnorderedMultiMap(
                   bslma::Allocator *basicAllocator);
        // Create an empty 'StripedUnorderedMultiMap' object, a fully
        // thread-safe hash map where access is partitioned into "stripes" (a
        // group of buckets protected a reader-writer mutex).  Optionally
        // specify 'numInitialBuckets' and 'numStripes' which define the
        // minimum number of buckets and the (fixed) number of stripes in this
        // map.  Optionally specify a 'basicAllocator' used to supply memory.
        // If 'basicAllocator' is 0, the currently installed default allocator
        // is used.  The hash map has rehash enabled.  Note that the number of
        // stripes will not change after construction, but the number of
        // buckets may (unless rehashing is disabled via 'disableRehash').

    //! ~StripedUnorderedMultiMap() = default;
        // Destroy this hash map.

    // MANIPULATORS
    void clear();
        // Remove all elements from this hash map.  If rehash is in progress,
        // block until it completes.

    void disableRehash();
        // Prevent future rehash until 'enableRehash' is called.

    void enableRehash();
        // Allow rehash.  If conditions warrant, rehash will be started by the
        // *next* method call that observes the load factor is exceeded (see
        // {Concurrent Rehash}).  Note that calling
        // 'maxLoadFactor(maxLoadFactor())' (i.e., setting the maximum load
        // factor to its current value) will trigger a rehash if needed but
        // otherwise does not change the hash map.

    bsl::size_t eraseAll(const KEY& key);
        // Erase from this hash map the elements having the specified 'key'.
        // Return the number of elements erased.

    template <class RANDOM_ITER>
    bsl::size_t eraseBulkAll(RANDOM_ITER first, RANDOM_ITER last);
        // Erase from this hash map elements in this hash map having any of the
        // values in the keys contained between the specified 'first'
        // (inclusive) and 'last' (exclusive) random-access iterators.  The
        // iterators provide read access to a sequence of 'KEY' objects.  All
        // erasures are done by the calling thread and the order of erasure is
        // not specified.  Return the number of elements removed.  The behavior
        // is undefined unless 'first <= last'.  Note that the map may not have
        // an element for every value in 'keys'.

    bsl::size_t eraseFirst(const KEY& key);
        // Erase from this hash map the *first* element (of possibly many)
        // found to the specified 'key'.  Return the number of elements erased.
        // Note that method is more performant than 'eraseAll' when there is
        // one element having 'key'.

    void insert(const KEY& key, const VALUE& value);
        // Insert into this hash map an element having the specified 'key' and
        // 'value'.  Note that other elements having the same 'key' may exist
        // in this hash map.

    void insert(const KEY& key, bslmf::MovableRef<VALUE> value);
        // Insert into this hash map an element having the specified 'key' and
        // the specified move-insertable 'value'.  The 'value' object is left
        // in a valid but unspecified state.  If 'value' is allocator-enabled
        // and 'allocator() != value.allocator()' this operation may cost as
        // much as a copy.  Note that other elements having the same 'key' may
        // exist in this hash map.

    template <class RANDOM_ITER>
    void insertBulk(RANDOM_ITER first, RANDOM_ITER last);
        // Insert into this hash map elements having the key-value pairs
        // obtained between the specified 'first' (inclusive) and 'last'
        // (exclusive) random-access iterators.  The iterators provide read
        // access to a sequence of 'bsl::pair<KEY, VALUE>' objects.  All
        // insertions are done by the calling thread and the order of insertion
        // is not specified.  The behavior is undefined unless 'first <= last'.

    void maxLoadFactor(float newMaxLoadFactor);
        // Set the maximum load factor of this unordered map to the specified
        // 'newMaxLoadFactor'.  If 'newMaxLoadFactor < loadFactor()', this
        // operation will cause an immediate rehash; otherwise, this operation
        // has a constant-time cost.  The rehash will increase the number of
        // buckets by a power of 2.  The behavior is undefined unless
        // '0 < newMaxLoadFactor'.

    void rehash(bsl::size_t numBuckets);
        // Recreate this hash map to one having at least the specified
        // 'numBuckets'.  This operation is a no-op if *any* of the following
        // are true: 1) rehash is disabled; 2) 'numBuckets' less or equals the
        // current number of buckets.  See {Rehash}.

    int setComputedValueAll(const KEY&             key,
                            const VisitorFunction& visitor);
        // Serially invoke the specified 'visitor' passing the specified 'key',
        // and the address of the value of each element in this hash map having
        // 'key'.  If 'key' is not in the map, 'value' will be default
        // constructed.  That is, for each '(key, value)' found, invoke:
        //..
        //  bool visitor(VALUE *value, const Key& key);
        //..
        // If no element in the map has 'key', insert '(key, VALUE())' and
        // invoke 'visitor' with 'value' pointing to the default constructed
        // value.  Return the number of elements visited or the negation of
        // that value if visitations stopped because 'visitor' returned
        // 'false'.  'visitor', when invoked, has exclusive access (i.e., write
        // access) to each element during each invocation.  The behavior is
        // undefined if hash map manipulators and 'getValue*' methods are
        // invoked from within 'visitor', as it may lead to a deadlock.  Note
        // that the 'setComputedValueFirst' method is more performant than the
        // when the hash map contains a single element for 'key'.  Also note
        // that a return value of '0' implies that an element was inserted.

    int setComputedValueFirst(const KEY&             key,
                              const VisitorFunction& visitor);
        // Invoke the specified 'visitor' passing the specified 'key', and the
        // address of the value attribute of the *first* element (of possibly
        // many elements) found in this hash map having 'key'.  If 'key' is not
        // in the map, 'value' will be default constructed.  That is, for
        // '(key, value)', invoke:
        //..
        //  bool visitor(VALUE *value, const Key& key);
        //..
        // If no element in the map has 'key', insert '(key, VALUE())' and
        // invoke 'visitor' with 'value' pointing to the default constructed
        // value.  Return 1 if 'key' was found and 'visitor' returned 'true', 0
        // if 'key' was not found, and -1 if 'key' was found and 'visitor'
        // returned 'false'.  'visitor', when invoked, has exclusive access
        // (i.e., write access) to the element.  The behavior is undefined if
        // hash map manipulators and 'getValue*' methods are invoked from
        // within 'visitor', as it may lead to a deadlock.  Note that the
        // return value equals the number of elements inserted.  Also note
        // that, when there are multiple elements having 'key', the selection
        // of "first" is implementation specific and subject to change.  Also
        // note that this method is more performant than the
        // 'setComputedValueAll' method when the hash map contains a single
        // element for 'key'.  Also note that a return value of '0' implies
        // that an element was inserted.

    bsl::size_t setValueAll(const KEY& key, const VALUE& value);
        // Set the value attribute of every element in this hash map having the
        // specified 'key' to the specified 'value'.  If no such such element
        // exists, insert '(key, value)'.  Return the number of elements found
        // with 'key'.  Note that if no elements were found, and a new value
        // was inserted, '0' is returned.

    bsl::size_t setValueFirst(const KEY& key, const VALUE& value);
        // Set the value attribute of the *first* element in this hash map (of
        // possibly many) found to have the specified 'key' to the specified
        // 'value'.  If no such such element exists, insert '(key, value)'.
        // Return the number of elements found with 'key'.  Note that if no
        // elements were found, and a new value was inserted, '0' is returned.
        // Also note that this method is more performant than 'setValueAll'
        // when there is one element having 'key' in the hash map.

    bsl::size_t setValueFirst(const KEY& key, bslmf::MovableRef<VALUE> value);
        // Set the value attribute of the *first* element in this hash map (of
        // possibly many) found to have the specified 'key' to the specified
        // move-insertable 'value'.  If no such such element exists, insert
        // '(key, value)'.  Return the number of elements found with 'key'.
        // The 'value' object is left in a valid but unspecified state.  If
        // 'value' is allocator-enabled and 'allocator() != value.allocator()'
        // this operation may cost as much as a copy.  Note that if no elements
        // were found, and a new value was inserted, '0' is returned.  Also
        // note that this method is more performant than 'setValueAll' when
        // there is one element having 'key' in the hash map.

    int update(const KEY& key, const VisitorFunction& visitor);
        // !DEPRECATED!: Use 'visit(key, visitor)' instead.
        //
        // Serially call the specified 'visitor' on each element (if one
        // exists) in this hash map having the specified 'key' until every such
        // element has been updated or 'visitor' returns 'false'.  That is, for
        // '(key, value)', invoke:
        //..
        //  bool visitor(&value, key);
        //..
        // Return the number of elements visited or the negation of that value
        // if visitations stopped because 'visitor' returned 'false'.
        // 'visitor' has exclusive access (i.e., write access) to each element
        // for duration of each invocation.  The behavior is undefined if hash
        // map manipulators and 'getValue*' methods are invoked from within
        // 'visitor', as it may lead to a deadlock.

    int visit(const VisitorFunction& visitor);
        // Call the specified 'visitor' (in an unspecified order) on the
        // elements in this hash table until each such element has been visited
        // or 'visitor' returns 'false'.  That is, for '(key, value)', invoke:
        //..
        //  bool visitor(&value, key);
        //..
        // Return the number of elements visited or the negation of that value
        // if visitations stopped because 'visitor' returned 'false'.
        // 'visitor' has exclusive access (i.e., write access) to each element
        // for duration of each invocation.  Every element present in this hash
        // map at the time 'visit' is invoked will be visited unless it is
        // removed before 'visitor' is called for that element.  Each
        // visitation is done by the calling thread and the order of visitation
        // is not specified.  Elements inserted during the execution of 'visit'
        // may or may not be visited.  The behavior is undefined if hash map
        // manipulators and 'getValue*' methods are invoked from within
        // 'visitor', as it may lead to a deadlock.  Note that 'visitor' can
        // change the value of the visited elements.

    int visit(const KEY& key, const VisitorFunction& visitor);
        // Serially call the specified 'visitor' on each element (if one
        // exists) in this hash map having the specified 'key' until every such
        // element has been updated or 'visitor' returns 'false'.  That is, for
        // '(key, value)', invoke:
        //..
        //  bool visitor(&value, key);
        //..
        // Return the number of elements visited or the negation of that value
        // if visitations stopped because 'visitor' returned 'false'.
        // 'visitor' has exclusive access (i.e., write access) to each element
        // for duration of each invocation.  The behavior is undefined if hash
        // map manipulators and 'getValue*' methods are invoked from within
        // 'visitor', as it may lead to a deadlock.

    // ACCESSORS
    bsl::size_t bucketCount() const;
        // Return the number of buckets in the array of buckets maintained by
        // this hash map.  Note that unless rehash is disabled, the value
        // returned may be obsolete by the time it is received.

    bsl::size_t bucketIndex(const KEY& key) const;
        // Return the index of the bucket, in the array of buckets maintained
        // by this hash map, where elements having the specified 'key' are
        // inserted.  Note that unless rehash is disabled, the value returned
        // may be obsolete at the time it is returned.

    bsl::size_t bucketSize(bsl::size_t index) const;
        // Return the number of elements contained in the bucket at the
        // specified 'index' in the array of buckets maintained by this hash
        // map.  The behavior is undefined unless
        // '0 <= index < bucketCount()'.  Note that unless rehash is disabled
        // the value returned may be obsolete by the time it is returned.

    bool empty() const;
        // Return 'true' if this hash map contains no elements, and 'false'
        // otherwise.

    EQUAL equalFunction() const;
        // Return (a copy of) the key-equality functor used by this hash map.
        // The returned function will return 'true' if two 'KEY' objects have
        // the same value, and 'false' otherwise.

    bsl::size_t getValueAll(bsl::vector<VALUE>      *valuesPtr,
                            const KEY&               key) const;
    bsl::size_t getValueAll(std::vector<VALUE>      *valuesPtr,
                            const KEY&               key) const;
#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
    bsl::size_t getValueAll(std::pmr::vector<VALUE> *valuesPtr,
                            const KEY&               key) const;
        // Load, into the specified '*valuesPtr', the value attributes of every
        // element in this hash map having the specified 'key'.  Return the
        // number of elements found with 'key'.  Note that the order of the
        // values returned is not specified.
#endif

    bsl::size_t getValueFirst(VALUE *value, const KEY& key) const;
        // Load, into the specified '*value', the value attribute of the first
        // element found in this hash map having the specified 'key'.  Return 1
        // on success, and 0 if 'key' does not exist in this hash map.  Note
        // that the return value equals the number of values returned.

    HASH hashFunction() const;
        // Return (a copy of) the unary hash functor used by this hash map.
        // The return function will generate a hash value (of type
        // 'std::size_t') for a 'KEY' object.

    bool isRehashEnabled() const;
        // Return 'true' if rehash is enabled, or 'false' otherwise.

    float loadFactor() const;
        // Return the current quotient of the size of this hash map and the
        // number of buckets.  Note that the load factor is a measure of
        // container "fullness"; that is, a high load factor typically implies
        // many collisions (many elements landing in the same bucket) and that
        // decreases performance.  See {Rehash Control}.

    float maxLoadFactor() const;
        // Return the maximum load factor allowed for this hash map.  If an
        // insert operation would cause the load factor to exceed the
        // 'maxLoadFactor()' and rehashing is enabled, then that insert
        // increases the number of buckets and rehashes the elements of the
        // container into that larger set of buckets.  See {Rehash Control}.

    bsl::size_t numStripes() const;
        // Return the number of stripes in the hash.

    int visitReadOnly(const ReadOnlyVisitorFunction& visitor) const;
        // Call the specified 'visitor' (in an unspecified order) on the
        // elements in this hash table until each such element has been visited
        // or 'visitor' returns 'false'.  That is, for '(key, value)', invoke:
        //..
        //  bool visitor(value, key);
        //..
        // Return the number of elements visited or the negation of that value
        // if visitations stopped because 'visitor' returned 'false'.
        // 'visitor' has read-only access to each element for duration of each
        // invocation.  Every element present in this hash map at the time
        // 'visit' is invoked will be visited unless it is removed before
        // 'visitor' is called for that element.  Each visitation is done by
        // the calling thread and the order of visitation is not specified. The
        // behavior is undefined if hash map manipulators are invoked from
        // within 'visitor', as it may lead to a deadlock.  Note that 'visitor'
        // can *not* change the value of the visited elements.

    int visitReadOnly(const KEY&                     key,
                      const ReadOnlyVisitorFunction& visitor) const;
        // Serially call the specified 'visitor' on each element (if one
        // exists) in this hash map having the specified 'key' until every such
        // element has been visited or 'visitor' returns 'false'.  That is, for
        // '(key, value)', invoke:
        //..
        //  bool visitor(value, key);
        //..
        // Return the number of elements visited or the negation of that value
        // if visitations stopped because 'visitor' returned 'false'.
        // 'visitor' has read-only access to each element for duration of each
        // invocation.  The behavior is undefined if hash map manipulators are
        // invoked from within 'visitor', as it may lead to a deadlock.

    bsl::size_t size() const;
        // Return the current number of elements in this hash map.

                               // Aspects

    bslma::Allocator *allocator() const;
        // Return the allocator used by this hash map to supply memory.  Note
        // that if no allocator was supplied at construction the default
        // allocator installed at that time is used.
};

// ============================================================================
//                             INLINE DEFINITIONS
// ============================================================================

                    // ------------------------------
                    // class StripedUnorderedMultiMap
                    // ------------------------------

// CREATORS
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::StripedUnorderedMultiMap(
                                           bsl::size_t       numInitialBuckets,
                                           bsl::size_t       numStripes,
                                           bslma::Allocator *basicAllocator)
: d_imp(numInitialBuckets, numStripes, basicAllocator)
{
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::StripedUnorderedMultiMap(
                                           bslma::Allocator *basicAllocator)
: d_imp(k_DEFAULT_NUM_BUCKETS, k_DEFAULT_NUM_STRIPES, basicAllocator)
{
}

// MANIPULATORS
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::clear()
{
    d_imp.clear();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::disableRehash()
{
    d_imp.disableRehash();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::enableRehash()
{
    d_imp.enableRehash();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::eraseAll(
                                                                const KEY& key)
{
    return d_imp.eraseAll(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
template <class RANDOM_ITER>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::eraseBulkAll(
                                                             RANDOM_ITER first,
                                                             RANDOM_ITER last)
{
    BSLS_ASSERT(first <= last);

    return d_imp.eraseBulkAll(first, last);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::eraseFirst(
                                                                const KEY& key)
{
    return d_imp.eraseFirst(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::insert(
                                                            const KEY&   key,
                                                            const VALUE& value)
{
    d_imp.insertAlways(key, value);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::insert(
                                                const KEY&               key,
                                                bslmf::MovableRef<VALUE> value)
{
    d_imp.insertAlways(key, bslmf::MovableRefUtil::move(value));
}

template <class KEY, class VALUE, class HASH, class EQUAL>
template <class RANDOM_ITER>
inline
void StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::insertBulk(
                                                             RANDOM_ITER first,
                                                             RANDOM_ITER last)
{
    BSLS_ASSERT(first <= last);

    d_imp.insertBulkAlways(first, last);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::maxLoadFactor(
                                                        float newMaxLoadFactor)
{
    BSLS_ASSERT(0 < newMaxLoadFactor);

    d_imp.maxLoadFactor(newMaxLoadFactor);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::rehash(
                                                        bsl::size_t numBuckets)
{
    d_imp.rehash(numBuckets);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int
StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::setComputedValueAll(
                                                const KEY&             key,
                                                const VisitorFunction& visitor)
{
    return d_imp.setComputedValueAll(key, visitor);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int
StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::setComputedValueFirst(
                                                const KEY&             key,
                                                const VisitorFunction& visitor)
{
    return d_imp.setComputedValueFirst(key, visitor);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::setValueAll(
                                                            const KEY&   key,
                                                            const VALUE& value)
{
    return d_imp.setValueAll(key, value);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::setValueFirst(
                                                            const KEY&   key,
                                                            const VALUE& value)
{
    return d_imp.setValueFirst(key, value);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::setValueFirst(
                                                const KEY&               key,
                                                bslmf::MovableRef<VALUE> value)
{
    return d_imp.setValueFirst(key, bslmf::MovableRefUtil::move(value));
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::update(
                                                const KEY&             key,
                                                const VisitorFunction& visitor)
{
    return d_imp.update(key, visitor);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::visit(
                                                const VisitorFunction& visitor)
{
    return d_imp.visit(visitor);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::visit(
                                                const KEY&             key,
                                                const VisitorFunction& visitor)
{
    return d_imp.visit(key, visitor);
}

// ACCESSORS
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t
StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::bucketCount() const
{
    return d_imp.bucketCount();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::bucketIndex(
                                                          const KEY& key) const
{
    return d_imp.bucketIndex(key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::bucketSize(
                                                       bsl::size_t index) const
{
    BSLS_ASSERT(bucketCount() > index);

    return d_imp.bucketSize(index);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bool StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::empty() const
{
    return d_imp.empty();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
EQUAL StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::equalFunction() const
{
    return d_imp.equalFunction();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::getValueAll(
                                                 bsl::vector<VALUE> *valuesPtr,
                                                 const KEY&          key) const
{
    return d_imp.getValue(valuesPtr, key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::getValueAll(
                                                 std::vector<VALUE> *valuesPtr,
                                                 const KEY&          key) const
{
    return d_imp.getValue(valuesPtr, key);
}

#ifdef BSLS_LIBRARYFEATURES_HAS_CPP17_PMR
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::getValueAll(
                                            std::pmr::vector<VALUE> *valuesPtr,
                                            const KEY&               key) const
{
    return d_imp.getValue(valuesPtr, key);
}
#endif

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::getValueFirst(
                                                         VALUE      *value,
                                                         const KEY&  key) const
{
    return d_imp.getValue(value, key);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
HASH StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::hashFunction() const
{
    return d_imp.hashFunction();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bool StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::isRehashEnabled() const
{
    return d_imp.isRehashEnabled();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
float StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::loadFactor() const
{
    return d_imp.loadFactor();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
float
StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::maxLoadFactor() const
{
    return d_imp.maxLoadFactor();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::numStripes()
                                                                          const
{
    return d_imp.numStripes();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::visitReadOnly(
                                  const ReadOnlyVisitorFunction& visitor) const
{
    return d_imp.visitReadOnly(visitor);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::visitReadOnly(
                                  const KEY&                     key,
                                  const ReadOnlyVisitorFunction& visitor) const
{
    return d_imp.visitReadOnly(key, visitor);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::size() const
{
    return d_imp.size();
}

                               // Aspects

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bslma::Allocator *StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL>::
                                                              allocator() const
{
    return d_imp.allocator();
}

}  // close package namespace

namespace bslma {

template <class KEY, class VALUE, class HASH, class EQUAL>
struct
UsesBslmaAllocator<bdlcc::StripedUnorderedMultiMap<KEY, VALUE, HASH, EQUAL> >
    : bsl::true_type {
};

}  // close namespace bslma

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2018 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License.  You may obtain a copy
// of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
// License for the specific language governing permissions and limitations
// under the License.
// ----------------------------- END-OF-FILE ----------------------------------