// 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
                      // ==============================

/// 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.
template <int t_SIZE>
class LocalSequentialAllocator : public BufferedSequentialAllocator {

    // 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
    /// 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`.
    union {
        char          d_buffer[t_SIZE];
        AlignmentType d_align;
    };
#endif

  private:
    // NOT IMPLEMENTED

    //! LocalSequentialAllocator(const LocalSequentialAllocator&) = delete;
    //! LocalSequentialAllocator& operator=(
    //!                              const LocalSequentialAllocator&) = delete;

  public:
    // CREATORS

    /// 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.
    explicit LocalSequentialAllocator(bslma::Allocator *basicAllocator = 0);

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

// ============================================================================
//                             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 ----------------------------------
