// bdlma_countingallocator.h                                          -*-C++-*-
#ifndef INCLUDED_BDLMA_COUNTINGALLOCATOR
#define INCLUDED_BDLMA_COUNTINGALLOCATOR

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

//@PURPOSE: Provide a memory allocator that counts allocated bytes.
//
//@CLASSES:
//  bdlma::CountingAllocator: concrete allocator that counts allocated bytes
//
//@SEE_ALSO: bslma_allocator, bslma_testallocator
//
//@DESCRIPTION: This component provides a special-purpose counting allocator,
// 'bdlma::CountingAllocator', that implements the 'bslma::Allocator' protocol
// and provides instrumentation to track: (1) the number of bytes currently in
// use ('numBytesInUse'), and (2) the cumulative number of bytes that have ever
// been allocated ('numBytesTotal').  The accumulated statistics are based
// solely on the number of bytes requested in calls to the 'allocate' method.
// A 'print' method is provided to output the current state of the allocator's
// byte counts to a specified 'bsl::ostream':
//..
//   ,------------------------.
//  ( bdlma::CountingAllocator )
//   `------------------------'
//                |           ctor/dtor
//                |           numBytesInUse
//                |           numBytesTotal
//                |           name
//                |           print
//                V
//       ,----------------.
//      ( bslma::Allocator )
//       `----------------'
//                            allocate
//                            deallocate
//..
// Like many other allocators, 'bdlma::CountingAllocator' relies on the
// currently installed default allocator (see 'bslma_default') at construction.
// Clients may, however, override this allocator by supplying (at construction)
// any other allocator implementing the 'bslma::Allocator' protocol provided
// that it is fully thread-safe.
//
// Note that a 'bdlma::CountingAllocator' necessarily incurs some overhead in
// order to provide its byte-counting functionality.  However, this overhead is
// *substantially* less than that incurred by the 'bslma::TestAllocator' (see
// 'bslma_testallocator'), which keeps track of the same two statistics that
// are maintained by a 'bdlma::CountingAllocator'.  Consequently, use of a
// 'bdlma::CountingAllocator' may be appropriate in cases where the overhead of
// 'bslma::TestAllocator' is too onerous.  In particular, a counting allocator
// may be suitable even for production use in certain situations, whereas the
// test allocator is not intended for production use under any circumstance.
//
///Byte Counts
///-----------
// The two byte counts maintained by 'bdlma::CountingAllocator' are initialized
// to 0 at construction and increased with each call to 'allocate' by 'size',
// i.e., by the actual number of bytes requested.  Each call to 'deallocate'
// decreases the 'numBytesInUse' count by the same amount by which the byte
// count was increased in the original 'allocate' call.  The number of bytes
// currently in use is returned by 'numBytesInUse' and the total number of
// bytes ever allocated is returned by 'numBytesTotal'.
//
///Thread Safety
///-------------
// The 'bdlma::CountingAllocator' class is fully thread-safe (see
// 'bsldoc_glossary') provided that the underlying allocator (established at
// construction) is fully thread-safe.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Tracking a Container's Dynamic Memory Use
///----------------------------------------------------
// In this example, we demonstrate how a counting allocator may be used to
// track the amount of dynamic memory used by a container.  The container used
// for illustration is 'DoubleStack', a stack of out-of-place 'double' values.
//
// First, we show the interface of the 'DoubleStack' class:
//..
//  // doublestack.h
//
//  class DoubleStack {
//      // This class implements a stack of out-of-place 'double' values.
//
//      // DATA
//      double           **d_stack_p;      // dynamically allocated array of
//                                         // 'd_capacity' elements
//
//      int                d_capacity;     // physical capacity of the stack
//                                         // (in elements)
//
//      int                d_length;       // logical index of next available
//                                         // stack element
//
//      bslma::Allocator  *d_allocator_p;  // memory allocator (held, not
//                                         // owned)
//
//      // NOT IMPLEMENTED
//      DoubleStack(const DoubleStack&);
//      DoubleStack& operator=(const DoubleStack&);
//
//    private:
//      // PRIVATE MANIPULATORS
//      void increaseCapacity();
//          // Increase the capacity of this stack by at least one element.
//
//    public:
//      // CREATORS
//      explicit
//      DoubleStack(bslma::Allocator *basicAllocator = 0);
//          // Create a stack for 'double' values having an initial capacity to
//          // hold one element.  Optionally specify a 'basicAllocator' used to
//          // supply memory.  If 'basicAllocator' is 0, the currently
//          // installed default allocator is used.
//
//      ~DoubleStack();
//          // Delete this object.
//
//      // MANIPULATORS
//      void push(double value);
//          // Add the specified 'value' to the top of this stack.
//
//      void pop();
//          // Remove the element at the top of this stack.  The behavior is
//          // undefined unless this stack is non-empty.
//
//      // ACCESSORS
//      // ...
//  };
//..
// Next, we show the (elided) implementation of 'DoubleStack'.
//
// The default constructor creates a stack having the capacity for one element
// (the implementation of the destructor is not shown):
//..
//  // doublestack.cpp
//  // ...
//
//  // TYPES
//  enum { k_INITIAL_CAPACITY = 1, k_GROWTH_FACTOR = 2 };
//
//  // CREATORS
//  DoubleStack::DoubleStack(bslma::Allocator *basicAllocator)
//  : d_stack_p(0)
//  , d_capacity(k_INITIAL_CAPACITY)
//  , d_length(0)
//  , d_allocator_p(bslma::Default::allocator(basicAllocator))
//  {
//      d_stack_p = (double **)
//                     d_allocator_p->allocate(d_capacity * sizeof *d_stack_p);
//  }
//..
// The 'push' method first ensures that the array has sufficient capacity to
// accommodate an additional value, then allocates a block in which to store
// that value:
//..
//  // MANIPULATORS
//  void DoubleStack::push(double value)
//  {
//      if (d_length >= d_capacity) {
//          increaseCapacity();
//      }
//      double *stackValue = (double *)d_allocator_p->allocate(sizeof(double));
//      *stackValue = value;
//      d_stack_p[d_length] = stackValue;
//      ++d_length;
//  }
//..
// The 'pop' method asserts that the stack is not empty before deallocating the
// block used to store the element at the top of the stack:
//..
//  void DoubleStack::pop()
//  {
//      BSLS_ASSERT(0 < d_length);
//
//      d_allocator_p->deallocate(d_stack_p[d_length - 1]);
//      --d_length;
//  }
//..
// The 'push' method (above) made use of the private 'increaseCapacity' method,
// which, in turn, makes use of the 'reallocate' helper function ('static' to
// the '.cpp' file).  Note that 'increaseCapacity' (below) increases the
// capacity of the 'double *' array by a factor of 2 each time that it is
// called:
//..
//  // HELPER FUNCTIONS
//  static
//  void reallocate(double           ***array,
//                  int                 newCapacity,
//                  int                 length,
//                  bslma::Allocator   *allocator)
//      // Reallocate memory in the specified 'array' to accommodate the
//      // specified 'newCapacity' elements using the specified 'allocator'.
//      // The specified 'length' number of leading elements are preserved.
//      // The behavior is undefined unless 'newCapacity > length'.
//  {
//      BSLS_ASSERT(newCapacity > length);
//
//      double **tmp = *array;
//      *array = (double **)allocator->allocate(newCapacity * sizeof **array);
//      bsl::memcpy(*array, tmp, length * sizeof **array);  // commit
//      allocator->deallocate(tmp);
//  }
//
//  // PRIVATE MANIPULATORS
//  void DoubleStack::increaseCapacity()
//  {
//      const int newCapacity = d_capacity * k_GROWTH_FACTOR;
//                                                      // reallocate can throw
//      reallocate(&d_stack_p, newCapacity, d_length, d_allocator_p);
//      d_capacity = newCapacity;                       // commit
//  }
//..
// Now, we are ready to employ a 'CountingAllocator' to illustrate the dynamic
// memory use of 'DoubleStack'.  We first define two constants that facilitate
// portability of this example across 32- and 64-bit platforms:
//..
//  const int DBLSZ = sizeof(double);
//  const int PTRSZ = sizeof(double *);
//..
// First, we define a 'CountingAllocator', 'ca'.  At construction, a counting
// allocator can be configured with an optional name and an optional allocator.
// In this case, we give 'ca' a name to distinguish it from other counting
// allocators, but settle for using the default allocator:
//..
//  bdlma::CountingAllocator ca("'DoubleStack' Allocator");
//..
// Next, we create a 'DoubleStack', supplying it with 'ca', and assert the
// expected memory use incurred by the default constructor:
//..
//  DoubleStack stack(&ca);
//                         assert(1 * PTRSZ             == ca.numBytesInUse());
//                         assert(1 * PTRSZ             == ca.numBytesTotal());
//..
// Next, we push an element onto the stack.  The first push incurs an
// additional allocation to store (out-of-place) the value being inserted:
//..
//  stack.push(1.54);      assert(1 * PTRSZ + 1 * DBLSZ == ca.numBytesInUse());
//                         assert(1 * PTRSZ + 1 * DBLSZ == ca.numBytesTotal());
//..
// Next, we push a second element onto the stack.  In this case, two
// allocations result, one due to the resizing of the internal array and one
// required to store the new value out-of-place:
//..
//  stack.push(0.99);      assert(2 * PTRSZ + 2 * DBLSZ == ca.numBytesInUse());
//                         assert(3 * PTRSZ + 2 * DBLSZ == ca.numBytesTotal());
//..
// Next, we pop the top-most element from the stack.  The number of bytes in
// use decreases by the amount used to store the popped element out-of-place:
//..
//  stack.pop();           assert(2 * PTRSZ + 1 * DBLSZ == ca.numBytesInUse());
//                         assert(3 * PTRSZ + 2 * DBLSZ == ca.numBytesTotal());
//..
// Finally, we print the state of 'ca' to standard output:
//..
//  ca.print(bsl::cout);
//..
// which displays the following on a 32-bit platform:
//..
//  ----------------------------------------
//                          Counting Allocator State
//  ----------------------------------------
//  Allocator name: 'DoubleStack' Allocator
//  Bytes in use:   16
//  Bytes in total: 28
//..

#include <bdlscm_version.h>

#include <bslma_allocator.h>

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

#include <bsl_iosfwd.h>

namespace BloombergLP {
namespace bdlma {

                         // =======================
                         // class CountingAllocator
                         // =======================

class CountingAllocator : public bslma::Allocator {
    // This class defines a concrete "counting" allocator mechanism that
    // implements the 'bslma::Allocator' protocol, and provides instrumentation
    // to track: (1) the number of bytes currently in use, and (2) the
    // cumulative number of bytes that have ever been allocated.  The
    // accumulated statistics are based solely on the number of bytes requested
    // (see 'allocate').
    //
    // Note that, like many other allocators, this allocator relies on the
    // currently installed default allocator (see 'bslma_default').  Clients
    // may, however, override this allocator by supplying (at construction) any
    // other allocator implementing the 'bslma::Allocator' protocol provided
    // that it is fully thread-safe.

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

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

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

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

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

  public:
    // CREATORS
    explicit
    CountingAllocator(bslma::Allocator *basicAllocator = 0);
    explicit
    CountingAllocator(const char *name, bslma::Allocator *basicAllocator = 0);
        // Create a counting allocator.  Optionally specify a 'name'
        // (associated with this object) to be included in messages output by
        // the 'print' method, thereby distinguishing this counting allocator
        // from others that might be used in the same program.  If 'name' is 0
        // (or not specified), no distinguishing name is incorporated in
        // 'print' output.  Optionally specify a 'basicAllocator' used to
        // supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.

    virtual ~CountingAllocator();
        // Destroy this allocator object.  Note that destroying this allocator
        // has no effect on any outstanding allocated memory.

    // MANIPULATORS
    virtual void *allocate(bsls::Types::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 with no other
        // effect (e.g., on allocation statistics).  Otherwise, invoke the
        // 'allocate' method of the allocator supplied at construction, and
        // increment the number of currently (and cumulatively) allocated bytes
        // by 'size'.

    virtual 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 (e.g., on
        // allocation statistics).  Otherwise, decrease the number of currently
        // allocated bytes by the size originally requested for the block.  The
        // behavior is undefined unless 'address' was allocated using this
        // allocator object and has not already been deallocated.

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

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

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

    bsl::ostream& print(bsl::ostream& stream) const;
        // Write the accumulated state information held in this allocator to
        // the specified 'stream' in some reasonable (multi-line) format, and
        // return a reference to 'stream'.
};

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

                         // -----------------------
                         // class CountingAllocator
                         // -----------------------

// ACCESSORS
inline
const char *CountingAllocator::name() const
{
    return d_name_p;
}

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

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

}  // close package namespace
}  // close enterprise namespace

#endif

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