// bdlcc_stripedunorderedmap.h                                        -*-C++-*-
#ifndef INCLUDED_BDLCC_STRIPEDUNORDEREDMAP
#define INCLUDED_BDLCC_STRIPEDUNORDEREDMAP

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

//@PURPOSE: Provide a bucket-group locking (i.e., *striped*) unordered map.
//
//@CLASSES:
//  bdlcc::StripedUnorderedMap: Striped hash map
//
//@SEE_ALSO: bdlcc_stripedunorderedmultimap,
//           bdlcc_stripedunorderedcontainerimpl
//
//@DESCRIPTION: This component provides a single concurrent (fully thread-safe)
// associative container, 'bdlcc::StripedUnorderedMap', 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_map' object protected by a single lock.
//
// The terms "bucket", "load factor", and "rehash" have the same meaning as
// they do in the 'bslstl_unorderedmap' component (see
// {'bslstl_unorderedmap'|Unordered Map Configuration}).  A general
// introduction to these ideas can be found at:
// https://en.wikipedia.org/wiki/Hash_table
//
// 'bdlcc::StripedUnorderedMap' (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' method 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::StripedUnorderedMap' 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::StripedUnorderedMap' 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, setValue, setComputedValue, update         | Average: O[1]      |
//  |                                                    | Worst:   O[n]      |
//  +----------------------------------------------------+--------------------+
//  | erase, getValue                                    | 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]    |
//  +----------------------------------------------------+--------------------+
//  | eraseBulk, 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 analogously named
// 'insert*' methods having semantics that are identical except for the meaning
// of the return value.  The rationale is best explained in the context of the
// 'bdlcc::StripedUnorderedMultiMap' class.  See
// {'bdlcc_stripedunorderedmultimap'|Set vs. Insert methods}.  The behavior as
// seen in *this* component is the degenerate case when the number of elements
// updated (or inserted) is limited to 0 or 1.
//
///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::StripedUnorderedMap'.
//
// First, we define a 'bdlcc::StripedUnorderedMap' object, 'myFriends', that
// maps 'int' to 'bsl::string':
//..
//  bdlcc::StripedUnorderedMap<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 'getValue' method to retrieve the previously inserted string
// associated with the value 1:
//..
//  bsl::string value;
//  bsl::size_t rc = myFriends.getValue(&value, 1);
//  assert(1      == rc);
//  assert("John" == value);
//..
// Now, we change the value associated with 1 from "John" to "Jack" and confirm
// that the size of the map has not changed:
//..
//  rc = myFriends.setValue(1, "Jack");
//  assert(1 == rc);
//  assert(6 == myFriends.size());
//
//  rc = myFriends.getValue(&value, 1);
//  assert(1      == rc);
//  assert("Jack" == value);
//..
// Finally, we erase the element '(3, "Jim")' from the map, confirm that the
// map size is decremented, and that element can no longer be found in the map:
//..
//  rc = myFriends.erase(3);
//  assert(1 == rc);
//  assert(5 == myFriends.size());
//
//  rc = myFriends.getValue(&value, 3);
//  assert(0 == rc);
//..
//
///Example 2: Track Stats
/// - - - - - - - - - - -
// This example uses the 'setComputedValue' and 'update' methods to keep track
// of user ID usage counts (stats).  A striped unordered map has the user ID as
// the key, and the count as the value.  There are 2 functors, one used to
// increase the count (and set it to '1' if the user ID has not been referenced
// yet), and the other is used to decrease the count.
//..
//  typedef bdlcc::StripedUnorderedMap<int, int> StatsMap;
//..
//
// First, define a functor, 'IncFunctor', that has parameters corresponding to
// the 'KEY' and 'VALUE' types of 'StatsMap' and, when invoked, adds 1 to
// whatever existing value is associated with the given 'KEY' value.  As a new
// value is initialized with default (0), adding 1 to it works correctly:
//..
//  struct IncFunctor {
//      bool operator()(int        *value,  // 'VALUE *'
//                      const int&)         // 'const KEY&'
//      {
//          *value += 1;
//          return true;
//      }
//  };
//..
//
// Next, define a functor, 'DecFunctor', that has parameters corresponding to
// the 'KEY' and 'VALUE' types of 'StatsMap' and, when invoked, subtracts 1
// from whatever existing value is associated with the given 'KEY' value:
//..
//      struct DecFunctor {
//          bool operator()(int        *value,  // 'VALUE *'
//                          const int&  )       // 'const KEY&'
//          {
//              *value -= 1;
//              return true;
//          }
//      };
//..
//
// Then, create 'myStats', a 'StatsMap' object with (as we did in {Example 1}
// default number of buckets, number of stripes, and allocator:
//..
//  StatsMap myStats;
//..
// Next, instantiate 'myIncFunctor' and 'myDecFunctor' from 'IncFunctor' and
// 'DecFunctor', respectively:
//..
//  IncFunctor myIncFunctor;
//  DecFunctor myDecFunctor;
//..
// Next, increase count for three user IDs:
//..
//  assert(0 == myStats.size());
//  int rc = myStats.setComputedValue(1001, myIncFunctor);
//  assert(0 == rc);
//  rc = myStats.setComputedValue(1002, myIncFunctor);
//  assert(0 == rc);
//  rc = myStats.setComputedValue(1003, myIncFunctor);
//  assert(0 == rc);
//  assert(3 == myStats.size());
//  int value = 0;
//  rc = myStats.getValue(&value, 1001);
//  assert(1 == rc);
//  assert(1 == value);
//..
// Now, increase count for existing user IDs.  Confirm that the values have
// been updated as expected.
//..
//  rc = myStats.setComputedValue(1001, myIncFunctor);
//  assert(1 == rc);
//  rc = myStats.setComputedValue(1002, myIncFunctor);
//  assert(1 == rc);
//  rc = myStats.setComputedValue(1001, myIncFunctor);
//  assert(1 == rc);
//  assert(3 == myStats.size());
//  rc = myStats.getValue(&value, 1001);
//  assert(1 == rc);
//  assert(3 == value);
//  rc = myStats.getValue(&value, 1002);
//  assert(1 == rc);
//  assert(2 == value);
//..
// Finally decrease count for existing user IDs.  Confirm that the values have
//  been updated as expected.
//..
//  int ret = myStats.update(1001, myDecFunctor);
//  assert(1 == ret);
//  ret = myStats.update(1003, myDecFunctor);
//  assert(1 == ret);
//  assert(3 == myStats.size());
//  rc = myStats.getValue(&value, 1001);
//  assert(1 == rc);
//  assert(2 == value);
//  rc = myStats.getValue(&value, 1003);
//  assert(1 == rc);
//  assert(0 == value);
//..
///Example 3: Visiting all the Container Elements
/// - - - - - - - - - - - - - - - - - - - - - - -
// This example uses the 'visit' method to apply a transformation (as defined
// by a functor) to the value of every key-value pair in the map.  This example
// will construct a map from names (type 'bsl::string') to some (arbitrary)
// measure of salary (type 'int'):
//..
//  typedef bdlcc::StripedUnorderedMap<bsl::string, int> SalaryMap;
//..
// First, define a functor, 'mySalaryAdjustmentVisitor', that increases values
// above 1000 by 3% and lower values by 5%.  The fractional part of increases
// are truncated to 'int' values:
//..
//  struct mySalaryAdjustmentVisitor {
//      bool operator()(int                *value,  // 'VALUE *'
//                      const bsl::string&)         // 'const KEY&'
//      {
//          if (*value <= 1000) {
//              *value = static_cast<int>(*value * 1.05);
//          } else {
//              *value = static_cast<int>(*value * 1.03);
//          }
//          return true;
//      }
//  };
//..
// Then, default create 'mySalaries', a 'SalaryMap' object:
//..
//  SalaryMap mySalaries;
//..
// Next, load 'mySalaries' with some representative elements:
//..
//  mySalaries.insert("Alex", 1000);
//  mySalaries.insert("John",  800);
//  mySalaries.insert("Rob",  1100);
//  assert(3 == mySalaries.size());
//..
// Now, apply 'mySalaryAdjustmentVisitor' to every element in the map:
//..
//  mySalaryAdjustmentVisitor func;
//  mySalaries.visit(func);
//  assert(3 == mySalaries.size());
//..
// Finally, confirm that the values have been adjusted as expected:
//..
//
//  int         value;
//  bsl::size_t rc;
//
//  rc = mySalaries.getValue(&value, "Alex");
//  assert(1    == rc);
//  assert(1050 == value);
//
//  rc = mySalaries.getValue(&value, "John");
//  assert(1    == rc);
//  assert( 840 == value);
//
//  rc = mySalaries.getValue(&value, "Rob");
//  assert(1    == rc);
//  assert(1133 == value);
//..

#include <bdlscm_version.h>

#include <bdlcc_stripedunorderedcontainerimpl.h>

#include <bslmf_movableref.h>

#include <bsls_assert.h>

#include <bsl_functional.h>

namespace BloombergLP {
namespace bdlcc {

                         // =========================
                         // class StripedUnorderedMap
                         // =========================

template <class KEY,
          class VALUE,
          class HASH  = bsl::hash<KEY>,
          class EQUAL = bsl::equal_to<KEY> >
class StripedUnorderedMap {
    // 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_map' object guarded by a single lock.
    //
    // The interface is inspired by, but not identical to that of
    // 'bsl::unordered_map'.  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
    StripedUnorderedMap(const StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>&);
                                                                    // = delete
    StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>&
                operator=(const StripedUnorderedMap<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 visitorFunction(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 value associated with 'key'
        //      // and 'value'.
        //..

    // CREATORS
    explicit StripedUnorderedMap(
                   bsl::size_t       numInitialBuckets = k_DEFAULT_NUM_BUCKETS,
                   bsl::size_t       numStripes        = k_DEFAULT_NUM_STRIPES,
                   bslma::Allocator *basicAllocator = 0);
    explicit StripedUnorderedMap(
                   bslma::Allocator *basicAllocator);
        // Create an empty 'StripedUnorderedMap' 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').

    //! ~StripedUnorderedMap() = 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 erase(const KEY& key);
        // Erase from this hash map the element having the specified 'key'.
        // Return 1 on success and 0 if 'key' does not exist.  Note that the
        // returned value equals the number of elements removed.

    template <class RANDOM_ITER>
    bsl::size_t eraseBulk(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 insert(const KEY& key, const VALUE& value);
        // Insert into this hash map an element having the specified 'key' and
        // 'value'.  If 'key' already exists in this hash map, the value
        // attribute of that element is set to 'value'.  Return 1 if an element
        // is inserted, and 0 if an existing element is updated.  Note that the
        // return value equals the number of elements inserted.

    bsl::size_t 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'.  If 'key' already exists in
        // this hash map, the value attribute of that element is set to
        // 'value'.  Return 1 if an element is inserted, and 0 if an existing
        // element is updated.  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 the return value equals the number of elements
        // inserted.

    template <class RANDOM_ITER>
    bsl::size_t 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.  If an
        // element having one of the keys already exists in this hash map, set
        // the value attribute to the corresponding value from 'data'.  All
        // insertions are done by the calling thread and the order of insertion
        // is not specified.  Return the number of elements inserted.  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 setComputedValue(const KEY&             key,
                         const VisitorFunction& visitor);
        // Invoke the specified 'visitor' on the value associated with the
        // specified 'key'.  The 'visitor' will be passed the address of the
        // value, and 'key'. If 'key' is not in the map, 'value' will be
        // default constructed.  That is, 'visitor' must be invocable with the
        // 'VisitorFunction' signature:
        //..
        //  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 found having 'key'.  Also
        // note that a return value of '0' implies that an element was
        // inserted.

    bsl::size_t setValue(const KEY& key, const VALUE& value);
        // Set the value attribute of the element in this hash map having the
        // specified 'key' to the specified 'value'.  If no such such element
        // exists, insert '(key, value)'.  Return 1 if 'key' was found, and 0
        // otherwise.  Note that the return value equals the number of elements
        // found having 'key'.

    bsl::size_t setValue(const KEY& key, bslmf::MovableRef<VALUE> value);
        // Set the value attribute of the element in this hash map having the
        // specified 'key' to the specified move-insertable 'value'.  If no
        // such such element exists, insert '(key, value)'.  Return 1 if 'key'
        // was found, and 0 otherwise.  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 the return value equals the number of elements
        // found having 'key'.

    int update(const KEY& key, const VisitorFunction& visitor);
        // !DEPRECATED!: Use 'visit(key, visitor)' instead.
        //
        // Call the specified 'visitor' with the element (if one exists) in
        // this hash map having the specified 'key'.  That is:
        //..
        //  bool visitor(&value, key);
        //..
        // Return the number of elements updated or -1 if 'visitor' returned
        // 'false'.  'visitor' has exclusive access (i.e., write access) the
        // element for during its 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 all
        // 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);
        // Call the specified 'visitor' with the element (if one exists) in
        // this hash map having the specified 'key'.  That is:
        //..
        //  bool visitor(&value, key);
        //..
        // Return the number of elements updated or -1 if 'visitor' returned
        // 'false'.  'visitor' has exclusive access (i.e., write access) the
        // element for during its 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 getValue(VALUE *value, const KEY& key) const;
        // Load, into the specified '*value', the value attribute of the
        // element 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 all
        // 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;
        // Call the specified 'visitor' on element (if one exists) in this hash
        // map having the specified 'key'.  That is, for '(key, value)',
        // invoke:
        //..
        //  bool visitor(value, key);
        //..
        // Return the number of elements visited or '-1' if '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 StripedUnorderedMap
                         // -------------------------

// CREATORS
template <class KEY, class VALUE, class HASH, class EQUAL>
inline
StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::StripedUnorderedMap(
                                           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
StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::StripedUnorderedMap(
                                           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 StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::clear()
{
    d_imp.clear();
}

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

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

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

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

    return d_imp.eraseBulkFirst(first, last);
}

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

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

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

    return d_imp.insertBulkUnique(first, last);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
void StripedUnorderedMap<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
StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::rehash(bsl::size_t numBuckets)
{
    d_imp.rehash(numBuckets);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::setComputedValue(
                                                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 StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::setValue(
                                                            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 StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::setValue(
                                                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 StripedUnorderedMap<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 StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::visit(
                                                const VisitorFunction& visitor)
{
    return d_imp.visit(visitor);
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int StripedUnorderedMap<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 StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::bucketCount() const
{
    return d_imp.bucketCount();
}

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bsl::size_t StripedUnorderedMap<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 StripedUnorderedMap<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 StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::empty() const
{
    return d_imp.empty();
}

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

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

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

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

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

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

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

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
int StripedUnorderedMap<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 StripedUnorderedMap<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 StripedUnorderedMap<KEY, VALUE, HASH, EQUAL>::size() const
{
    return d_imp.size();
}

                               // Aspects

template <class KEY, class VALUE, class HASH, class EQUAL>
inline
bslma::Allocator *StripedUnorderedMap<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::StripedUnorderedMap<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 ----------------------------------