// bslma_testallocator.h                                              -*-C++-*-
#ifndef INCLUDED_BSLMA_TESTALLOCATOR
#define INCLUDED_BSLMA_TESTALLOCATOR

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

//@PURPOSE: Provide instrumented malloc/free allocator to track memory usage.
//
//@CLASSES:
//  bslma::TestAllocator: instrumented 'malloc'/'free' memory allocator
//
//@MACROS:
//  BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN: macro to begin testing exceptions
//  BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END: macro to end testing exceptions
//
//@SEE_ALSO: bslma_newdeleteallocator, bslma_mallocfreeallocator,
//  balst_stacktracetestallocator
//
//@DESCRIPTION: This component provides an instrumented allocator,
// 'bslma::TestAllocator', that implements the 'bslma::Allocator' protocol and
// can be used to track various aspects of memory allocated from it.  Available
// statistics include the number of outstanding blocks (and bytes) that are
// currently in use, the cumulative number of blocks (and bytes) that have been
// allocated, and the maximum number of blocks (and bytes) that have been in
// use at any one time.  A 'print' function formats these values to 'stdout':
//..
//   ,--------------------.
//  ( bslma::TestAllocator )
//   `--------------------'
//             |         ctor/dtor
//             |         lastAllocatedAddress/lastDeallocatedAddress
//             |         lastAllocatedNumBytes/lastDeallocatedNumBytes
//             |         numAllocations/numDeallocations
//             |         numBlocksInUse/numBlocksMax/numBlocksTotal
//             |         numBytesInUse/numBytesMax/numBytesTotal
//             |         numMismatches/numBoundsErrors
//             |         print/name
//             |         setAllocationLimit/allocationLimit
//             |         setNoAbort/isNoAbort
//             |         setQuiet/isQuiet
//             |         setVerbose/isVerbose
//             |         status
//             V
//     ,----------------.
//    ( bslma::Allocator )
//     `----------------'
//                       allocate
//                       deallocate
//..
// If exceptions are enabled, this allocator can be configured to throw an
// exception after the number of allocation requests exceeds some specified
// limit (see the subsection on "Allocation Limit" below).  The level of
// verbosity can also be adjusted.  Each allocator object also maintains a
// current status.
//
// By default this allocator gets its memory from the C Standard Library
// functions 'malloc' and 'free', but can be overridden to take memory from any
// allocator (supplied at construction) that implements the 'bslma::Allocator'
// protocol.  Note that allocation and deallocation using a
// 'bslma::TestAllocator' object is explicitly incompatible with 'malloc' and
// 'free' (or any other allocation mechanism).  Attempting to use 'free' to
// deallocate memory allocated from a 'bslma::TestAllocator' -- even when
// 'malloc' and 'free' are used by default -- will result in undefined
// behavior, almost certainly corrupting the C Standard Library's runtime
// memory manager.
//
// Memory dispensed from a 'bslma::TestAllocator' is marked such that
// attempting to deallocate previously unallocated (or already deallocated)
// memory will (with high probability) be flagged as an error (unless quiet
// mode is set for the purpose of testing the test allocator itself).  A
// 'bslma::TestAllocator' also supports a buffer overrun / underrun feature --
// each allocation has "pads", areas of extra memory before and after the
// segment that are initialized to a particular value and checked upon
// deallocation to see if they have been modified.  If they have, a message is
// printed and the allocator aborts, unless it is in quiet mode.
//
///Detecting Memory Leaks
///----------------------
// The 'bslma::TestAllocator' is useful for detecting memory leaks, unless
// configured in quiet mode.  With the default configuration, if a test
// allocator is destroyed before all memory is reclaimed, a report will be
// logged and 'abort' will be called.  When such a memory leak is detected,
// clients can substitute 'balst::StackTraceTestAllocator' for
// 'bslma::TestAllocator' to report stack traces of allocations that were
// leaked.  Note that 'balst::StackTraceTestAllocator' is slower and consumes
// more memory than 'bslma::TestAllocator', and usually is not appropriate for
// automated tests.
//
///Modes
///-----
// The test allocator's behavior is controlled by three basic *mode* flags:
//
// VERBOSE MODE: (Default 0) Specifies that each allocation and deallocation
// should be printed to standard output.  In verbose mode all state variables
// will be displayed at destruction.
//
// QUIET MODE: (Default 0) Specifies that mismatched memory and memory leaks
// should *not* be reported, and should not cause the process to terminate when
// detected.  Note that this mode is used primarily for testing the test
// allocator itself; behavior that would otherwise abort now quietly increments
// the 'numMismatches' and 'numBoundsErrors' counter.
//
// NO-ABORT MODE: (Default 0) Specifies that the test allocator should not
// invoke 'abort' under any circumstances without suppressing diagnostics.
// Although the internal state values are independent, quiet mode implies the
// behavior of no-abort mode in all cases.  Note that this mode is used
// primarily for visual inspection of unusual error diagnostics in this
// component's test driver (in non-quiet mode only).
//
// Taking the default mode settings, memory allocation/deallocation will not be
// displayed individually.  However, in the event of a mismatched deallocation
// or a memory leak detected at destruction, the problem will be announced, any
// relevant state of the object will be displayed, and the program will abort.
//
// The three modes are independently set using the 'setVerbose', 'setQuiet',
// and 'setNoAbort' manipulators.
//
///Allocation Limit
///----------------
// If exceptions are enabled at compile time, the test allocator can be
// configured to throw a 'bslma::TestAllocatorException' after a specified
// number of allocation requests is exceeded.  If the allocation limit is less
// than 0 (default), then the allocator won't throw a 'TestAllocatorException'
// exception.  Note that a non-negative allocation limit is decremented after
// each allocation attempt, and an exception is thrown only when the current
// allocation limit transitions from 0 to -1; no additional exceptions will be
// thrown until the allocation limit is again reset to a non-negative value.
//
// The allocation limit is set using the 'setAllocationLimit' manipulator.
//
///Exception Test Macros
///---------------------
// This component also provides a pair of macros:
//
//: o 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(BSLMA_TESTALLOCATOR)'
//: o 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END'
//
// These macros can be used for testing exception-safety of classes and their
// methods when memory allocation is needed.  A reference to an object of type
// 'bslma::TestAllocator' must be supplied as an argument to the '_BEGIN'
// macro.  Note that if exception-handling is disabled (i.e., if
// 'BDE_BUILD_TARGET_EXC' is not defined when building the code under test),
// then the macros simply print the following:
//..
//  BSLMA EXCEPTION TEST -- (NOT ENABLED) --
//..
// When exception-handling is enabled, the '_BEGIN' macro will set the
// allocation limit of the supplied allocator to 0, 'try' the code being
// tested, 'catch' any 'TestAllocatorException's that are thrown, and keep
// increasing the allocation limit until the code being tested completes
// successfully.
//
///Thread Safety
///-------------
// The 'bslma::TestAllocator' class is fully thread-safe (see
// 'bsldoc_glossary').  Note that the 'bslma::MallocFreeAllocator' singleton
// (the allocator used by the test allocator if none is supplied at
// construction) is fully thread-safe.
//
///Usage
///-----
// The 'bslma::TestAllocator' defined in this component can be used in
// conjunction with the 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN' and
// 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END' macros to test the memory usage
// patterns of an object that uses the 'bslma::Allocator' protocol in its
// interface.  In this example, we illustrate how we might test that an object
// under test is exception-neutral.  For illustration purposes, we will assume
// the existence of a 'my_shortarray' component implementing an
// 'std::vector'-like array type, 'myShortArray':
//..
//  // my_shortarray.t.cpp
//  #include <my_shortarray.h>
//
//  #include <bslma_testallocator.h>
//  #include <bslma_testallocatorexception.h>
//
//  // ...
//..
// Below we provide a 'static' function, 'areEqual', that will allow us to
// compare two short arrays:
//..
//  static
//  bool areEqual(const short *array1, const short *array2, int numElements)
//      // Return 'true' if the specified initial 'numElements' in the
//      // specified 'array1' and 'array2' have the same values, and 'false'
//      // otherwise.
//  {
//      for (int i = 0; i < numElements; ++i) {
//          if (array1[i] != array2[i]) {
//              return false;                                         // RETURN
//          }
//      }
//      return true;
//  }
//
//  // ...
//..
// The following is an abbreviated standard test driver.  Note that the number
// of arguments specify the verbosity level that the test driver uses for
// printing messages:
//..
//  int main(int argc, char *argv[])
//  {
//      int                 test = argc > 1 ? atoi(argv[1]) : 0;
//      bool             verbose = argc > 2;
//      bool         veryVerbose = argc > 3;
//      bool     veryVeryVerbose = argc > 4;
//      bool veryVeryVeryVerbose = argc > 5;
//..
// We now define a 'bslma::TestAllocator', 'sa', named "supplied" to indicate
// that it is the allocator to be supplied to our object under test, as well as
// to the 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN' macro (below).  Note that
// if 'veryVeryVeryVerbose' is 'true', then 'sa' prints all allocation and
// deallocation requests to 'stdout' and also prints the accumulated statistics
// on destruction:
//..
//  bslma::TestAllocator sa("supplied", veryVeryVeryVerbose);
//
//  switch (test) { case 0:
//
//    // ...
//
//    case 6: {
//
//      // ...
//
//      struct {
//          int   d_line;
//          int   d_numElem;
//          short d_exp[NUM_VALUES];
//      } DATA[] = {
//          { L_, 0, { } },
//          { L_, 1, { V0 } },
//          { L_, 5, { V0, V1, V2, V3, V4 } }
//      };
//      const int NUM_DATA = sizeof DATA / sizeof *DATA;
//
//      for (int ti = 0; ti < NUM_DATA; ++ti) {
//          const int    LINE     = DATA[ti].d_line;
//          const int    NUM_ELEM = DATA[ti].d_numElem;
//          const short *EXP      = DATA[ti].d_exp;
//
//          if (veryVerbose) { T_ P_(ti) P_(NUM_ELEM) }
//
//          // ...
//..
// All code that we want to test for exception-safety must be enclosed within
// the 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN' and
// 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END' macros, which internally implement
// a 'do'-'while' loop.  Code provided by the
// 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN' macro sets the allocation limit
// of the supplied allocator to 0 causing it to throw an exception on the first
// allocation.  This exception is caught by code provided by the
// 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END' macro, which increments the
// allocation limit by 1 and re-runs the same code again.  Using this scheme we
// can check that our code does not leak memory for any memory allocation
// request.  Note that the curly braces surrounding these macros, although
// visually appealing, are not technically required:
//..
//      BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(sa) {
//        my_ShortArray mA(&sa);
//        const my_ShortArray& A = mA;
//        for (int ei = 0; ei < NUM_ELEM; ++ei) {
//            mA.append(VALUES[ei]);
//        }
//        if (veryVerbose) { T_ T_  P_(NUM_ELEM) P(A) }
//        LOOP_ASSERT(LINE, areEqual(EXP, A, NUM_ELEM));
//      } BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END
//  }
//..
// After the exception-safety test we can ensure that all the memory allocated
// from 'sa' was successfully deallocated:
//..
//          if (veryVerbose) sa.print();
//
//        } break;
//
//        // ...
//
//      }
//
//      // ...
//  }
//..
// Note that the 'BDE_BUILD_TARGET_EXC' macro is defined at compile-time to
// indicate whether or not exceptions are enabled.

#include <bslscm_version.h>

#include <bslma_allocator.h>
#include <bslma_testallocatorexception.h>

#include <bsls_atomic.h>
#include <bsls_bsllock.h>
#include <bsls_buildtarget.h>
#include <bsls_types.h>

#include <cstdio>     // for printing in macros

namespace BloombergLP {
namespace bslma {

struct TestAllocator_List;

                             // ===================
                             // class TestAllocator
                             // ===================

class TestAllocator : public Allocator {
    // This class defines a concrete "test" allocator mechanism that implements
    // the 'Allocator' protocol, and provides instrumentation to track (1) the
    // number of blocks/bytes currently in use, (2) the maximum number of
    // blocks/bytes that have been outstanding at any one time, and (3) the
    // cumulative number of blocks/bytes that have ever been allocated by this
    // test allocator object.  The accumulated statistics are based solely on
    // the number of bytes requested.  Additional testing facilities include
    // allocation limits, verbosity modes, status, and automated report
    // printing.
    //
    // Note that, unlike many other allocators, this allocator does NOT rely on
    // the currently installed default allocator (see 'bslma_default'), but
    // instead -- by default -- uses the 'MallocFreeAllocator' singleton, which
    // in turn calls the C Standard Library functions 'malloc' and 'free' as
    // needed.  Clients may, however, override this allocator by supplying (at
    // construction) any other allocator implementing the 'Allocator' protocol.

    // DATA

                        // Control Points

    const char *d_name_p;                // optionally specified name of this
                                         // test allocator object (or 0)

    bsls::AtomicInt
                d_noAbortFlag;           // whether or not to suppress
                                         // aborting on fatal errors

    bsls::AtomicInt
                d_quietFlag;             // whether or not to suppress
                                         // reporting hard errors

    bsls::AtomicInt
                d_verboseFlag;           // whether or not to report
                                         // allocation/deallocation events and
                                         // print statistics on destruction

    bsls::AtomicInt64
                d_allocationLimit;       // number of allocations before
                                         // exception is thrown by this object

                        // Statistics

    bsls::AtomicInt64
                d_numAllocations;        // total number of allocation
                                         // requests on this object (including
                                         // those for 0 bytes)

    bsls::AtomicInt64
                d_numDeallocations;      // total number of deallocation
                                         // requests on this object (including
                                         // those supplying a 0 address)

    bsls::AtomicInt64
                d_numMismatches;         // number of mismatched memory
                                         // deallocation errors encountered by
                                         // this object
    bsls::AtomicInt64
                d_numBoundsErrors;       // number of overrun/underrun errors
                                         // encountered by this object

    bsls::AtomicInt64
                d_numBlocksInUse;        // number of blocks currently
                                         // allocated from this object

    bsls::AtomicInt64
                d_numBytesInUse;         // number of bytes currently
                                         // allocated from this object

    bsls::AtomicInt64
                d_numBlocksMax;          // maximum number of blocks ever
                                         // allocated from this object at any
                                         // one time

    bsls::AtomicInt64
                d_numBytesMax;           // maximum number of bytes ever
                                         // allocated from this object at any
                                         // one time

    bsls::AtomicInt64
                d_numBlocksTotal;        // cumulative number of blocks ever
                                         // allocated from this object

    bsls::AtomicInt64
                d_numBytesTotal;         // cumulative number of bytes ever
                                         // allocated from this object

                        // Other Data

    bsls::AtomicInt64
                d_lastAllocatedNumBytes; // size (in bytes) of the most recent
                                         // allocation request

    bsls::AtomicInt64
                d_lastDeallocatedNumBytes;
                                         // size (in bytes) of the most
                                         // recently deallocated memory

    bsls::AtomicPointer<int>
                d_lastAllocatedAddress_p;// address of the most recently
                                         // allocated memory (or 0)

    bsls::AtomicPointer<int>
                d_lastDeallocatedAddress_p;
                                         // address of the most recently
                                         // deallocated memory (or 0)

    TestAllocator_List
               *d_list_p;                // list of allocated memory (owned)

    mutable bsls::BslLock
                d_lock;                  // ensure mutual exclusion in
                                         // 'allocate', 'deallocate', 'print',
                                         // and 'status'

    Allocator  *d_allocator_p;           // memory allocator (held, not owned)

  private:
    // NOT IMPLEMENTED
    TestAllocator(const TestAllocator&);             // = delete
    TestAllocator& operator=(const TestAllocator&);  // = delete

  public:
    // CREATORS
    explicit
    TestAllocator(Allocator  *basicAllocator = 0);
    explicit
    TestAllocator(const char *name,
                  Allocator  *basicAllocator = 0);
    explicit
    TestAllocator(bool        verboseFlag,
                  Allocator  *basicAllocator = 0);
    TestAllocator(const char *name,
                  bool        verboseFlag,
                  Allocator  *basicAllocator = 0);
        // Create an instrumented "test" allocator.  Optionally specify a
        // 'name' (associated with this object) to be included in diagnostic
        // messages written to 'stdout', thereby distinguishing this test
        // allocator from others that might be used in the same program.  If
        // 'name' is 0 (or not specified), no distinguishing name is
        // incorporated in diagnostics.  Optionally specify a 'verboseFlag'
        // indicating whether this test allocator should automatically report
        // all allocation/deallocation events to 'stdout' and print accumulated
        // statistics on destruction.  If 'verboseFlag' is 'false' (or not
        // specified), allocation/deallocation and summary messages will not be
        // written automatically.  Optionally specify a 'basicAllocator' used
        // to supply memory.  If 'basicAllocator' is 0, the
        // 'MallocFreeAllocator' singleton is used.

    ~TestAllocator();
        // Destroy this allocator.  In verbose mode, print all contained state
        // values of this allocator object to 'stdout'.  Except in quiet mode,
        // automatically report any memory leaks to 'stdout'.  Abort if either
        // 'numBlocksInUse' or 'numBytesInUse' return non-zero unless in
        // no-abort mode or quiet mode.  Note that, in all cases, destroying
        // this object has no effect on outstanding memory blocks allocated
        // from this test allocator (and may result in memory leaks -- e.g., if
        // the (default) 'MallocFreeAllocator' singleton was used).

    // MANIPULATORS
    void *allocate(size_type size);
        // Return a newly-allocated block of memory of the specified 'size' (in
        // bytes).  If 'size' is 0, a null pointer is returned.  Otherwise,
        // invoke the 'allocate' method of the allocator supplied at
        // construction, increment the number of currently (and cumulatively)
        // allocated blocks, and increase the number of currently allocated
        // bytes by 'size'.  Update all other fields accordingly.

    void deallocate(void *address);
        // Return the memory block at the specified 'address' back to this
        // allocator.  If 'address' is 0, this function has no effect (other
        // than to record relevant statistics).  Otherwise, if the memory at
        // 'address' is consistent with being allocated from this test
        // allocator, decrement the number of currently allocated blocks, and
        // decrease the number of currently allocated bytes by the size (in
        // bytes) originally requested for the block.  Although technically
        // undefined behavior, if the memory can be determined not to have been
        // allocated from this test allocator, increment the number of
        // mismatches, and -- unless in quiet mode -- immediately report the
        // details of the mismatch to 'stdout' (e.g., as an 'std::hex' memory
        // dump) and abort.

    void setAllocationLimit(bsls::Types::Int64 limit);
        // Set the number of valid allocation requests before an exception is
        // to be thrown for this allocator to the specified 'limit'.  If
        // 'limit' is less than 0, no exception is to be thrown.  By default,
        // no exception is scheduled.

    void setNoAbort(bool flagValue);
        // Set the no-abort mode for this test allocator to the specified
        // (boolean) 'flagValue'.  'If flagValue' is 'true', aborting on fatal
        // errors is suppressed, and the functions simply return.  Diagnostics
        // are not affected.  Note that the default mode is to abort.  Also
        // note that this function is provided primarily to enable visual
        // testing of diagnostic messages produced by this component.

    void setQuiet(bool flagValue);
        // Set the quiet mode for this test allocator to the specified
        // (boolean) 'flagValue'.  If 'flagValue' is 'true', mismatched
        // allocations, overrun/underrun errors, and memory leak messages will
        // not be displayed to 'stdout' and the process will not abort as a
        // result of such conditions.  Note that the default mode is *not*
        // quiet.  Also note that this function is provided primarily to enable
        // testing of this component; in quiet mode, situations that would
        // otherwise abort will just quietly increment the 'numMismatches'
        // and/or 'numBoundsErrors' counters.

    void setVerbose(bool flagValue);
        // Set the verbose mode for this test allocator to the specified
        // (boolean) 'flagValue'.  If 'flagValue' is 'true', all
        // allocation/deallocation events will be reported automatically on
        // 'stdout', as will accumulated statistics upon destruction of this
        // object.  Note that the default mode is *not* verbose.

    // ACCESSORS
    bsls::Types::Int64 allocationLimit() const;
        // Return the current number of allocation requests left before an
        // exception is thrown.  A negative value indicates that no exception
        // is scheduled.

    bool isNoAbort() const;
        // Return 'true' if this allocator is currently in no-abort mode, and
        // 'false' otherwise.  In no-abort mode all diagnostic messages are
        // printed, but all aborts are suppressed.  Note that quiet mode
        // implies no-abort mode.

    bool isQuiet() const;
        // Return 'true' if this allocator is currently in quiet mode, and
        // 'false' otherwise.  In quiet mode, messages about mismatched
        // deallocations, overrun/underrun errors, and memory leaks will not be
        // displayed to 'stdout' and will not cause the program to abort.

    bool isVerbose() const;
        // Return 'true' if this allocator is currently in verbose mode, and
        // 'false' otherwise.  In verbose mode, all allocation/deallocation
        // events will be reported on 'stdout', as will summary statistics upon
        // destruction of this object.

    void *lastAllocatedAddress() const;
        // Return the address that was returned by the most recent allocation
        // request.  Return 0 if the most recent allocation request was for 0
        // bytes.

    size_type lastAllocatedNumBytes() const;
        // Return the number of bytes of the most recent allocation request.

    void *lastDeallocatedAddress() const;
        // Return the address that was supplied to the most recent deallocation
        // request.  Return 0 if a null pointer was most recently deallocated.
        // Note that the address is always recorded regardless of the validity
        // of the request.

    size_type lastDeallocatedNumBytes() const;
        // Return the number of bytes of the most recent deallocation request.
        // Return 0 if a null pointer was most recently deallocated, or if the
        // request was invalid (e.g., an attempt to deallocate memory not
        // allocated through this allocator).

    const char *name() const;
        // Return the name of this test allocator, or 0 if no name was
        // specified at construction.

    bsls::Types::Int64 numAllocations() const;
        // Return the cumulative number of allocation requests.  Note that this
        // number is incremented for every 'allocate' invocation.

    bsls::Types::Int64 numBlocksInUse() const;
        // Return the number of blocks currently allocated from this object.
        // Note that 'numBlocksInUse() <= numBlocksMax()'.

    bsls::Types::Int64 numBlocksMax() const;
        // Return the maximum number of blocks ever allocated from this object
        // at any one time.  Note that
        // 'numBlocksInUse() <= numBlocksMax() <= numBlocksTotal()'.

    bsls::Types::Int64 numBlocksTotal() const;
        // Return the cumulative number of blocks ever allocated from this
        // object.  Note that 'numBlocksMax() <= numBlocksTotal()'.

    bsls::Types::Int64 numBoundsErrors() const;
        // Return the number of times memory deallocations have detected that
        // pad areas at the front or back of the user segment had been
        // overwritten.

    bsls::Types::Int64 numBytesInUse() const;
        // Return the number of bytes currently allocated from this object.
        // Note that 'numBytesInUse() <= numBytesMax()'.

    bsls::Types::Int64 numBytesMax() const;
        // Return the maximum number of bytes ever allocated from this object
        // at any one time.  Note that
        // 'numBytesInUse() <= numBytesMax() <= numBytesTotal()'.

    bsls::Types::Int64 numBytesTotal() const;
        // Return the cumulative number of bytes ever allocated from this
        // object.  Note that 'numBytesMax() <= numBytesTotal()'.

    bsls::Types::Int64 numDeallocations() const;
        // Return the cumulative number of deallocation requests.  Note that
        // this number is incremented for every 'deallocate' invocation,
        // regardless of the validity of the request.

    bsls::Types::Int64 numMismatches() const;
        // Return the number of mismatched memory deallocations that have
        // occurred since this object was created.  A memory deallocation is
        // *mismatched* if that memory was not allocated directly from this
        // allocator.

    void print() const;
        // Write the accumulated state information held in this allocator to
        // 'stdout' in some reasonable (multi-line) format.

    int status() const;
        // Return 0 on success, and non-zero otherwise: If there have been any
        // mismatched memory deallocations or over/under runs, return the
        // number of such errors that have occurred as a positive number; if
        // either '0 < numBlocksInUse()' or '0 < numBytesInUse()', return an
        // arbitrary negative number; else return 0.

#ifndef BDE_OPENSOURCE_PUBLICATION  // DEPRECATED
    void *lastAllocateAddress() const;
        // Return the allocated memory address of the most recent memory
        // request.  Return 0 if the request was invalid (e.g., allocate non-
        // positive number of bytes).
        //
        // DEPRECATED: use 'lastAllocatedAddress' instead.

    size_type lastAllocateNumBytes() const;
        // Return the number of bytes of the most recent memory request.  Note
        // that this number is always recorded regardless of the validity of
        // the request.
        //
        // DEPRECATED: use 'lastAllocatedNumBytes' instead.

    void *lastDeallocateAddress() const;
        // Return the memory address of the last memory deallocation request.
        // Note that the address is always recorded regardless of the validity
        // of the request.
        //
        // DEPRECATED: use 'lastDeallocatedAddress' instead.

    size_type lastDeallocateNumBytes() const;
        // Return the number of bytes of the most recent memory deallocation
        // request.  Return 0 if the request was invalid (e.g., deallocating
        // memory not allocated through this allocator).
        //
        // DEPRECATED: use 'lastDeallocatedNumBytes' instead.

    bsls::Types::Int64 numAllocation() const;
        // Return the cumulative number of allocation requests.  Note that this
        // number is incremented for every 'allocate' invocation, regardless of
        // the validity of the request.
        //
        // DEPRECATED: use 'numAllocations' instead.

    bsls::Types::Int64 numDeallocation() const;
        // Return the cumulative number of deallocation requests.  Note that
        // this number is incremented for every 'deallocate' invocation,
        // regardless of the validity of the request.
        //
        // DEPRECATED: use 'numDeallocations' instead.
#endif  // BDE_OPENSOURCE_PUBLICATION
};

}  // close package namespace

               // ==============================================
               // macro BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN
               // ==============================================

#ifdef BDE_BUILD_TARGET_EXC

namespace bslma {

class TestAllocator_ProxyBase {
    // This class provides a common base class for the parameterized
    // 'TestAllocator_Proxy' class (below).  Note that the 'virtual'
    // 'setAllocationLimit' method, although a "setter", *must* be declared
    // 'const'.

  public:
    // CREATOR
    virtual ~TestAllocator_ProxyBase()
    {
    }

    // ACCESSORS
    virtual void setAllocationLimit(bsls::Types::Int64 limit) const = 0;
};

template <class BSLMA_ALLOC_TYPE>
class TestAllocator_Proxy : public TestAllocator_ProxyBase {
    // This class provides a proxy to the test allocator that is supplied to
    // the 'BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN' macro.  This proxy may be
    // instantiated with 'TestAllocator', or with a type that supports the same
    // interface as 'TestAllocator'.

    // DATA
    BSLMA_ALLOC_TYPE *d_allocator_p;  // allocator used in '*_BEGIN' and
                                      // '*_END' macros (held, not owned)

  public:
    // CREATORS
    explicit TestAllocator_Proxy(BSLMA_ALLOC_TYPE *allocator)
    : d_allocator_p(allocator)
    {
    }

    ~TestAllocator_Proxy()
    {
    }

    // ACCESSORS
    virtual void setAllocationLimit(bsls::Types::Int64 limit) const
    {
        d_allocator_p->setAllocationLimit(limit);
    }
};

template <class BSLMA_ALLOC_TYPE>
inline
TestAllocator_Proxy<BSLMA_ALLOC_TYPE>
TestAllocator_getProxy(BSLMA_ALLOC_TYPE *allocator)
    // Return, by value, a test allocator proxy for the specified parameterized
    // 'allocator'.
{
    return TestAllocator_Proxy<BSLMA_ALLOC_TYPE>(allocator);
}

}  // close package namespace

#ifndef BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN
// Note that the `while` loop in the following code uses a flag
// `bslmaKeepLoopingInTestAllocatorExceptionTest`.  This is a workaround for an
// XLC16 bug: a `continue` statement in a `catch` block can result in
// segementation faults on optimized XLC16 builds.  Bug raised with IBM - see
// DRQS 169604597
#define BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(BSLMA_TESTALLOCATOR) {     \
    {                                                                       \
        static int firstTime = 1;                                           \
        if (veryVerbose && firstTime) {                                     \
            std::puts("\t\tBSLMA EXCEPTION TEST -- (ENABLED) --");          \
        }                                                                   \
        firstTime = 0;                                                      \
    }                                                                       \
    if (veryVeryVerbose) {                                                  \
        std::puts("\t\tBegin bslma exception test.");                       \
    }                                                                       \
    int bslmaExceptionCounter = 0;                                          \
    const BloombergLP::bslma::TestAllocator_ProxyBase&                      \
        bslmaExceptionTestAllocator =                                       \
          BloombergLP::bslma::TestAllocator_getProxy(&BSLMA_TESTALLOCATOR); \
    bslmaExceptionTestAllocator.setAllocationLimit(bslmaExceptionCounter);  \
    bool bslmaKeepLoopingInTestAllocatorExceptionTest = true;               \
    while(bslmaKeepLoopingInTestAllocatorExceptionTest) {                   \
        bslmaKeepLoopingInTestAllocatorExceptionTest = false;               \
        try {
#endif  // BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN

#else   // !defined(BDE_BUILD_TARGET_EXC)

#ifndef BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN
#define BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(BSLMA_TESTALLOCATOR)       \
{                                                                           \
    static int firstTime = 1;                                               \
    if (verbose && firstTime) {                                             \
        std::puts("\t\tBSLMA EXCEPTION TEST -- (NOT ENABLED) --");          \
        firstTime = 0;                                                      \
    }                                                                       \
}
#endif  // BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN

#endif  // BDE_BUILD_TARGET_EXC

                 // ============================================
                 // macro BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END
                 // ============================================

#ifdef BDE_BUILD_TARGET_EXC

#ifndef BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END
#define BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END                              \
        } catch (BloombergLP::bslma::TestAllocatorException& e) {           \
            if (veryVeryVerbose) {                                          \
                std::printf("\t*** BSLMA_EXCEPTION: "                       \
                            "alloc limit = %d, last alloc size = %d ***\n", \
                            bslmaExceptionCounter,                          \
                            static_cast<int>(e.numBytes()));                \
            }                                                               \
            bslmaExceptionTestAllocator.setAllocationLimit(                 \
                                                 ++bslmaExceptionCounter);  \
            bslmaKeepLoopingInTestAllocatorExceptionTest = true;            \
        }                                                                   \
    };                                                                      \
    bslmaExceptionTestAllocator.setAllocationLimit(-1);                     \
    if (veryVeryVerbose) {                                                  \
        std::puts("\t\tEnd bslma exception test.");                         \
    }                                                                       \
}

#endif  // BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END

#else   // !defined(BDE_BUILD_TARGET_EXC)

#ifndef BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END
#define BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END
#endif

#endif

namespace bslma {

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

                        // -------------------
                        // class TestAllocator
                        // -------------------

// MANIPULATORS
inline
void TestAllocator::setAllocationLimit(bsls::Types::Int64 limit)
{
    d_allocationLimit.storeRelaxed(limit);
}

inline
void TestAllocator::setNoAbort(bool flagValue)
{
    d_noAbortFlag.storeRelaxed(flagValue);
}

inline
void TestAllocator::setQuiet(bool flagValue)
{
    d_quietFlag.storeRelaxed(flagValue);
}

inline
void TestAllocator::setVerbose(bool flagValue)
{
    d_verboseFlag.storeRelaxed(flagValue);
}

// ACCESSORS
inline
bsls::Types::Int64 TestAllocator::allocationLimit() const
{
    return d_allocationLimit.loadRelaxed();
}

inline
bool TestAllocator::isNoAbort() const
{
    return d_noAbortFlag.loadRelaxed();
}

inline
bool TestAllocator::isQuiet() const
{
    return d_quietFlag.loadRelaxed();
}

inline
bool TestAllocator::isVerbose() const
{
    return d_verboseFlag.loadRelaxed();
}

inline
void *TestAllocator::lastAllocatedAddress() const
{
    return reinterpret_cast<void *>(d_lastAllocatedAddress_p.loadRelaxed());
}

inline
Allocator::size_type TestAllocator::lastAllocatedNumBytes() const
{
    return static_cast<size_type>(d_lastAllocatedNumBytes.loadRelaxed());
}

inline
void *TestAllocator::lastDeallocatedAddress() const
{
    return reinterpret_cast<void *>(d_lastDeallocatedAddress_p.loadRelaxed());
}

inline
Allocator::size_type TestAllocator::lastDeallocatedNumBytes() const
{
    return static_cast<size_type>(d_lastDeallocatedNumBytes.loadRelaxed());
}

inline
const char *TestAllocator::name() const
{
    return d_name_p;
}

inline
bsls::Types::Int64 TestAllocator::numAllocations() const
{
    return d_numAllocations.loadRelaxed();
}

inline
bsls::Types::Int64 TestAllocator::numBlocksInUse() const
{
    return d_numBlocksInUse.loadRelaxed();
}

inline
bsls::Types::Int64 TestAllocator::numBlocksMax() const
{
    return d_numBlocksMax.loadRelaxed();
}

inline
bsls::Types::Int64 TestAllocator::numBlocksTotal() const
{
    return d_numBlocksTotal.loadRelaxed();
}

inline
bsls::Types::Int64 TestAllocator::numBoundsErrors() const
{
    return d_numBoundsErrors.loadRelaxed();
}

inline
bsls::Types::Int64 TestAllocator::numBytesInUse() const
{
    return d_numBytesInUse.loadRelaxed();
}

inline
bsls::Types::Int64 TestAllocator::numBytesMax() const
{
    return d_numBytesMax.loadRelaxed();
}

inline
bsls::Types::Int64 TestAllocator::numBytesTotal() const
{
    return d_numBytesTotal.loadRelaxed();
}

inline
bsls::Types::Int64 TestAllocator::numDeallocations() const
{
    return d_numDeallocations.loadRelaxed();
}

inline
bsls::Types::Int64 TestAllocator::numMismatches() const
{
    return d_numMismatches.loadRelaxed();
}

#ifndef BDE_OPENSOURCE_PUBLICATION  // DEPRECATED
inline
void *TestAllocator::lastAllocateAddress() const
{
    return lastAllocatedAddress();
}

inline
TestAllocator::size_type
TestAllocator::lastAllocateNumBytes() const
{
    return lastAllocatedNumBytes();
}

inline
void *TestAllocator::lastDeallocateAddress() const
{
    return lastDeallocatedAddress();
}

inline
TestAllocator::size_type
TestAllocator::lastDeallocateNumBytes() const
{
    return lastDeallocatedNumBytes();
}

inline
bsls::Types::Int64 TestAllocator::numAllocation() const
{
    return numAllocations();
}

inline
bsls::Types::Int64 TestAllocator::numDeallocation() const
{
    return numDeallocations();
}

#endif  // BDE_OPENSOURCE_PUBLICATION

}  // close package namespace

#ifndef BDE_OPENSOURCE_PUBLICATION  // BACKWARD_COMPATIBILITY
// ============================================================================
//                           BACKWARD COMPATIBILITY
// ============================================================================

typedef bslma::TestAllocator bslma_TestAllocator;
    // This alias is defined for backward compatibility.

// The following two macros can be deleted when they are no longer referenced
// in any .t.cpp files.

#ifndef BEGIN_BSLMA_EXCEPTION_TEST
#define BEGIN_BSLMA_EXCEPTION_TEST                                     \
              BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(testAllocator)
#endif

#ifndef END_BSLMA_EXCEPTION_TEST
#define END_BSLMA_EXCEPTION_TEST BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END
#endif

#endif  // BDE_OPENSOURCE_PUBLICATION -- BACKWARD_COMPATIBILITY

}  // close enterprise namespace

#endif

// ----------------------------------------------------------------------------
// Copyright 2013 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 ----------------------------------