// bdlma_localsequentialallocator.h                                   -*-C++-*-
#ifndef INCLUDED_BDLMA_LOCALSEQUENTIALALLOCATOR
#define INCLUDED_BDLMA_LOCALSEQUENTIALALLOCATOR

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

//@PURPOSE: Provide an efficient managed allocator using a local buffer.
//
//@CLASSES:
//  bdlma::LocalSequentialAllocator: allocator using a local buffer
//
//@SEE_ALSO: bdlma_bufferedsequentialallocator
//
//@DESCRIPTION: This component provides a concrete mechanism,
// 'bdlma::LocalSequentialAllocator', that implements the
// 'bdlma::ManagedAllocator' protocol to very efficiently allocate
// heterogeneous memory blocks (of varying, user-specified sizes) from a local
// buffer.  Note that it derives from 'bdlma::BufferedSequentialAllocator' so
// that the implementations of 'allocate', 'deallocate', and 'release' don't
// need to be instantiated for each user-specified size.
//..
//    ,-------------------------------.
//   ( bdlma::LocalSequentialAllocator )
//    `-------------------------------'
//                    |        ctor
//                    V
//   ,----------------------------------.
//  ( bdlma::BufferedSequentialAllocator )
//   `----------------------------------'
//                    |        ctor/dtor
//                    |        allocate
//                    |        deallocate
//                    |        release
//                    |        rewind
//                    V
//        ,-----------------------.
//       ( bdlma::ManagedAllocator )
//        `-----------------------'
//                    |        release = 0
//                    V
//           ,----------------.
//          ( bslma::Allocator )
//           `----------------'
//                             allocate = 0
//                             deallocate = 0
//..
// If an allocation request exceeds the remaining free memory space in the
// local buffer, the allocator will fall back to a sequence of
// dynamically-allocated buffers.  The 'release' method releases all memory
// allocated through the allocator, as does the destructor.  Note that, even
// though a 'deallocate' method is available, it has no effect: individually
// allocated memory blocks cannot be separately deallocated.
//
// 'bdlma::LocalSequentialAllocator' is typically used when users have a
// reasonable estimation of the amount of memory needed.  This amount of memory
// would then be created directly on the program stack, and used as the local
// buffer by this allocator for very fast memory allocation.  Whilst the buffer
// has sufficient capacity, memory allocations will not trigger *any* dynamic
// memory allocation, will have optimal locality of reference, and will not
// require deallocation upon destruction.
//
// Once the local buffer is exhausted, subsequent allocation requests require
// dynamic memory allocation, and the performance of the allocator degrades.
//
// The main difference between a 'bdlma::LocalSequentialAllocator' and a
// 'bdlma::BufferedSequentialAllocator' is that this class internally maintains
// a buffer, rather than being given one at construction time.
//
///Usage
///-----
// This section illustrates intended use of this component.
//
///Example 1: Recommended Usage
/// - - - - - - - - - - - - - -
// Suppose we have a function which takes a map of items to update in some
// database:
//..
//  typedef bsl::string DatabaseKey;
//  typedef bsl::string DatabaseValue;
//
//  void updateRecords_1(const bsl::map<DatabaseKey, DatabaseValue>& values)
//  {
//      for (bsl::map<DatabaseKey, DatabaseValue>::const_iterator
//               it = values.begin(), end = values.end();
//           it != end;
//           ++it) {
//          bsl::stringbuf stringBuf;
//          bsl::ostream   ostr(&stringBuf);
//
//          ostr << "UPDATE myTable SET myValue = '" << it->first << "' WHERE "
//                  "myKey = '" << it->second << "'";
//
//          // execute query using 'stringBuf.str()'
//      }
//  }
//..
// We call this method a lot, and after profiling, we notice that it's
// contributing a significant proportion of time, due to the allocations it is
// making.  We decide to see whether a LocalSequentialAllocator would help.
//
// First, use a 'bslma::TestAllocator' to track the typical memory usage:
//..
//  void updateRecords_2(const bsl::map<DatabaseKey, DatabaseValue>& values)
//  {
//      bslma::TestAllocator ta;
//
//      for (bsl::map<DatabaseKey, DatabaseValue>::const_iterator
//               it = values.begin(), end = values.end();
//           it != end;
//           ++it) {
//          bsl::stringbuf stringBuf(&ta);
//          bsl::ostream   ostr(&stringBuf);
//
//          ostr << "UPDATE myTable SET myValue = '" << it->first << "' WHERE "
//                  "myKey = '" << it->second << "'";
//
//          // execute query using 'stringBuf.str()'
//
//          bsl::cout << "In use: " << ta.numBytesInUse() << '\n';
//      }
//
//      bsl::cout << "Max: " << ta.numBytesMax() << '\n';
//  }
//..
// Then we run our program again, and observe the following output:
//..
//  In use: 77
//  In use: 77
//  In use: 77
//  In use: 77
//  In use: 77
//  Max: 129
//..
// It looks like 129 is a good choice for the size of our allocator, so we go
// with that:
//..
//  void updateRecords_3(const bsl::map<DatabaseKey, DatabaseValue>& values)
//  {
//      bdlma::LocalSequentialAllocator<129> lsa;
//
//      for (bsl::map<DatabaseKey, DatabaseValue>::const_iterator
//               it = values.begin(), end = values.end();
//           it != end;
//           ++it) {
//          lsa.release();
//
//          bsl::stringbuf stringBuf(&lsa);
//          bsl::ostream   ostr(&stringBuf);
//
//          ostr << "UPDATE myTable SET myValue = '" << it->first << "' WHERE "
//                  "myKey = '" << it->second << "'";
//
//          // execute query using 'stringBuf.str()'
//      }
//  }
//..
// Note that we release at the end of every iteration, as the deallocate method
// is a no-op, so without this, subsequent memory would be allocated from the
// default allocator (or the allocator passed to 'bsa' at construction).
//
// Finally, we re-profile our code to determine whether the addition of a
// 'LocalSequentialAllocator' helped.

#include <bdlscm_version.h>

#include <bdlma_bufferedsequentialallocator.h>

#include <bslma_allocator.h>

namespace BloombergLP {
namespace bdlma {

                      // ==============================
                      // class LocalSequentialAllocator
                      // ==============================

template <int t_SIZE>
class LocalSequentialAllocator : public BufferedSequentialAllocator {
    // This class implements the 'ManagedAllocator' protocol to provide a fast
    // allocator that dispenses heterogeneous blocks of memory (of varying,
    // user-specified sizes) from a local buffer whose capacity is the
    // specified 't_SIZE' (in bytes).  If an allocation request exceeds the
    // remaining free memory space in the local buffer, memory will be supplied
    // by an (optional) allocator supplied at construction; if no allocator is
    // supplied, the currently installed default allocator is used.  This class
    // is *exception* *neutral*; if memory cannot be allocated, the behavior is
    // defined by the (optional) allocator supplied at construction.

    // PRIVATE TYPES
    typedef BufferedSequentialAllocator                          AllocatorBase;
#if !defined(BSLS_COMPILERFEATURES_SUPPORT_ALIGNAS)
    typedef bsls::AlignmentToType<
                  bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT>::Type AlignmentType;
#endif

    // DATA


#if defined(BSLS_COMPILERFEATURES_SUPPORT_ALIGNAS)
    // The C++11 implementation uses the 'alignas' keyword to ensure the
    // alignment of 'd_buffer'.
    alignas(bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT) char d_buffer[t_SIZE];
#else
    union {
        // This anonymous union is 'bsls::AlignedBuffer', but typed out again
        // so that extra template instantiations are avoided.  The C++03
        // implementation uses a union data member to ensure the alignment of
        // 'd_buffer'.
        char          d_buffer[t_SIZE];
        AlignmentType d_align;
    };
#endif

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

  public:
    // CREATORS
    explicit LocalSequentialAllocator(bslma::Allocator *basicAllocator = 0);
        // Create a local sequential allocator for allocating memory blocks
        // from a local buffer having the specified 't_SIZE' (in bytes).
        // Optionally specify a 'basicAllocator' used to supply memory should
        // the capacity of the local buffer be exhausted.  If 'basicAllocator'
        // is 0, the currently installed default allocator is used.

    //! virtual ~LocalSequentialAllocator() = default;
        // Destroy this local sequential allocator.  All memory allocated from
        // this allocator is released.
};

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

                      // ------------------------------
                      // class LocalSequentialAllocator
                      // ------------------------------

// CREATORS
template <int t_SIZE>
inline
LocalSequentialAllocator<t_SIZE>::LocalSequentialAllocator(
                                              bslma::Allocator *basicAllocator)
: AllocatorBase(this->d_buffer, t_SIZE, basicAllocator)
{
}

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

#endif

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