// bslma_bufferallocator.h                                            -*-C++-*-
#ifndef INCLUDED_BSLMA_BUFFERALLOCATOR
#define INCLUDED_BSLMA_BUFFERALLOCATOR

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

//@PURPOSE: Support efficient memory allocations from a user-supplied buffer.
//
//@INTERNAL_DEPRECATED: Use `bdlma_bufferedsequentialallocator` instead.
//
//@CLASSES:
//  bslma::BufferAllocator: memory allocator from user-supplied buffer
//
//@SEE_ALSO: bdlma_bufferedsequentialallocator
//
//@DESCRIPTION: This component provides an allocator, `bslma::BufferAllocator`,
// that implements the `bslma::Allocator` protocol and sequentially allocates
// memory blocks from a fixed-size buffer that is supplied by the user at
// construction.  If an allocation request exceeds the remaining space in the
// buffer, the return value is the result of invoking an optional callback
// function that was supplied at construction, or zero if no callback was
// specified.  This component also provides static utility functions for
// allocating memory directly from a user-specified buffer:
// ```
//  ,----------------------.
// ( bslma::BufferAllocator )
//  `----------------------'
//             |         ctor
//             V
//     ,----------------.
//    ( bslma::Allocator )
//     `----------------'
//                     allocate
//                     deallocate
//                     dtor
// ```
//
///Alignment Strategy
///------------------
// The `bslma::BufferAllocator` allocates memory using one of the two alignment
// strategies: 1) MAXIMUM ALIGNMENT or 2) NATURAL ALIGNMENT.
//
// 1. MAXIMUM ALIGNMENT: This strategy always allocates memory aligned with the
//    most restrictive alignment on the host platform.  The value is defined in
//    `bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT`.
// 2. NATURAL ALIGNMENT: This strategy allocates memory whose alignment depends
//    on the requested number of bytes.  An object of a fundamental type
//    (`int`, etc.) is *naturally* *aligned* when it's size evenly divides its
//    address.  An object of an aggregate type has natural alignment if the
//    alignment of the most-restrictively aligned sub-object evenly divides the
//    address of the aggregate.  Natural alignment is always at least as
//    restrictive as the compiler's required alignment.  When only the size of
//    an aggregate is known, and not its composition, we compute the alignment
//    by finding the largest integral power of 2 (up to and including
//    `bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT`) that divides the requested
//    (non-zero) number of bytes.  This computed alignment is guaranteed to be
//    at least as restrictive as any sub-object within the aggregate.
//
///Usage
///-----
// The `bslma::BufferAllocator` class defined in this component is commonly
// used to allocate memory from a static buffer that does not require
// "deletion".  The following snippet of code creates an array that obtains its
// memory from a fixed-sized buffer, but through the `bslma::Allocator`
// protocol:
// ```
// // my_shortarray.h
//
// // ...
//
// namespace bslma { class Allocator; }
//
// class my_ShortArray {
//     short            *d_array_p;      // dynamically-allocated array of
//                                       // `short` integers
//
//     int               d_size;         // physical size of `d_array_p`
//
//     int               d_length;       // logical length of `d_array_p`
//
//     bslma::Allocator *d_allocator_p;  // memory allocator (held, not owned)
//
//   private:
//     // PRIVATE MANIPULATORS
//     void increaseSize();  // Increase the capacity by at least one element.
//
//   public:
//     // CREATORS
//
//     /// Create an empty array using the specified `basicAllocator` to
//     /// supply memory.
//     my_ShortArray(bslma::Allocator *basicAllocator);
//     // ...
//
//     ~my_ShortArray();
//     void append(short value);
//     const short& operator[](int index) const { return d_array_p[index]; }
//     int length() const { return d_length; }
// };
// ```
// The implementation of `my_ShortArray` is as follows:
// ```
// // my_shortarray.cpp
// #include <my_shortarray.h>
//
// enum { INITIAL_SIZE = 1, GROW_FACTOR = 2 };
// // ...
//
// my_ShortArray::my_ShortArray(bslma::Allocator *basicAllocator)
// : d_size(INITIAL_SIZE)
// , d_length(0)
// , d_allocator_p(basicAllocator)
// {
//     assert(d_allocator_p);
//     d_array_p =
//         (short *) d_allocator_p->allocate(d_size * sizeof *d_array_p);
// }
//
// my_ShortArray::~my_ShortArray()
// {
//     // CLASS INVARIANTS
//     assert(d_array_p);
//     assert(0 <= d_size);
//     assert(0 <= d_length); assert(d_length <= d_size);
//     assert(d_allocator_p);
//
//     d_allocator_p->deallocate(d_array_p);
// }
//
// inline
// void my_ShortArray::append(short value)
// {
//     if (d_length >= d_size) {
//         increaseSize();
//     }
//     d_array_p[d_length++] = value;
// }
//
// /// Reallocate memory in the specified `array` to the specified
// /// `newSize` using the specified `basicAllocator` or global new
// /// operator.  The specified `length` number of leading elements are
// /// preserved.  Since the class invariant requires that the physical
// /// capacity of the container may grow but never shrink, the behavior
// /// is undefined unless length <= newSize.
// static
// void reallocate(short **array, int newSize, int length,
//                 bslma::Allocator *basicAllocator)
// {
//     assert(array);
//     assert(1 <= newSize);
//     assert(0 <= length);
//     assert(length <= newSize);        // enforce class invariant
//     assert(basicAllocator);
//
//     short *tmp = *array;              // support exception neutrality
//     *array = (short *) basicAllocator->allocate(newSize * sizeof **array);
//
//     // COMMIT POINT
//
//     memcpy(*array, tmp, length * sizeof **array);
//     basicAllocator->deallocate(tmp);
// }
//
// void my_ShortArray::increaseSize()
// {
//      int proposedNewSize = d_size * GROW_FACTOR;    // reallocate can throw
//      assert(proposedNewSize > d_length);
//      reallocate(&d_array_p, proposedNewSize, d_length, d_allocator_p);
//      d_size = proposedNewSize;                      // we're committed
// }
// // ...
// ```
// Use buffer allocator to allocate memory for `my_ShortArray` as follows:
// ```
// void *callbackFunc(int /* size */)
// {
//     std::cerr << "Buffer allocator out of memory." << std::endl;
//     exit(-1);
//     return (void *) 0;
// }
// // ...
//
// int main(int argc, char *argv[])
// {
// // ...
//     static char memory[1024];  // No alignment issue; see below.
//     bslma::BufferAllocator allocator(memory, sizeof memory, callbackFunc);
//
//     my_ShortArray mA(allocator);
//     const my_ShortArray& A = mA;
//     mA.append(123);
//     mA.append(456);
// // ...
// }
// ```
// Note that in the `main` function above, `memory` does not need to be aligned
// because `bslma::BufferAllocator::allocate` internally performs alignment for
// each requested memory block based on the allocator's alignment strategy.

#ifdef BDE_OPENSOURCE_PUBLICATION // DEPRECATED
#error "bslma_bufferallocator is deprecated"
#endif
#include <bslscm_version.h>

#include <bslma_allocator.h>

#include <bsls_keyword.h>

namespace BloombergLP {

namespace bslma {

                        // =====================
                        // class BufferAllocator
                        // =====================

/// This `class` provides a concrete buffer allocator that implements the
/// `Allocator` interface, and allocates memory blocks from a fixed-size
/// buffer that is supplied by the user at construction.  The allocator can
/// supply memory that can be maximally (default) or naturally aligned.
class BufferAllocator : public Allocator {

  public:
    // TYPES

    /// `AllocCallback` is an alias for (the address of) a function that
    /// takes an integer argument and returns `void *`.
    typedef void *(*AllocCallback)(int);

    /// Types of alignment strategy to allocate memory.
    enum AlignmentStrategy {

        MAXIMUM_ALIGNMENT,  // Default.  Return memory on maximally-aligned
                            // boundary
                            // 'bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT' (as
                            // required for ANSI standard allocators).

        NATURAL_ALIGNMENT   // Return memory aligned on a boundary that is the
                            // largest power of two dividing the requested size
                            // (up to
                            // 'bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT').
    };

  private:
    // DATA
    AlignmentStrategy  d_strategy;      // strategy to use for alignment
    int                d_cursor;        // position of the remaining free space
    char              *d_buffer_p;      // buffer for allocating memory
    int                d_bufferSize;    // size of the buffer
    AllocCallback      d_allocCallback; // function handling buffer overflow

    // NOT IMPLEMENTED
    BufferAllocator(const BufferAllocator&);
    BufferAllocator& operator=(const BufferAllocator&);

  public:
    // CLASS METHODS

    /// Allocate a memory block of the specified `size` from the specified
    /// `buffer` at the specified `cursor` position.  Return the address of
    /// the allocated memory block if `buffer` contains enough available
    /// memory, and `null` otherwise.  The `cursor` is set to the index of
    /// the next available byte in `buffer` after the allocation.  Use the
    /// specified alignment `strategy` (or `alignment` value) to allocate
    /// memory.  If `size` is 0, do not allocate memory and return zero.
    /// The behavior is undefined unless `0 < = bufSize`, `0 <= size`,
    /// `0 < alignment <= bsls::AlignmentUtil::BSLS_MAX_ALIGNMENT`, and
    /// alignment is an integral power of 2.
    static void *allocateFromBuffer(int               *cursor,
                                    char              *buffer,
                                    size_type          bufSize,
                                    size_type          size,
                                    AlignmentStrategy  strategy);
    static void *allocateFromBuffer(int       *cursor,
                                    char      *buffer,
                                    size_type  bufSize,
                                    size_type  size,
                                    int        alignment);

    // CREATORS

    /// Create a buffer allocator for allocating memory blocks from the
    /// specified `buffer` of the specified `bufSize`.  Optionally specify a
    /// callback function `allocCallback` that is invoked when a call to
    /// `allocate` cannot be fulfilled from `buffer`.  Note that maximum
    /// alignment is used when allocating memory.
    BufferAllocator(char          *buffer,
                    size_type      bufSize,
                    AllocCallback  allocCallback = 0);

    /// Create a buffer allocator for allocating memory blocks from the
    /// specified `buffer` of the specified `bufSize`.  Use the specified
    /// alignment `strategy` to allocate memory.  Optionally specify a
    /// callback function `allocCallback` that is invoked when a call to
    /// `allocate` cannot be fulfilled from `buffer`.
    BufferAllocator(char              *buffer,
                    size_type          bufSize,
                    AlignmentStrategy  strategy,
                    AllocCallback      allocCallback = 0);

    /// Destroy this buffer allocator.
    ~BufferAllocator() BSLS_KEYWORD_OVERRIDE;

    // MANIPULATORS

    /// Return a newly allocated block of memory of (at least) the specified
    /// positive `size` (in bytes).  If `size` is 0, a null pointer is
    /// returned with no other effect.  If this allocator cannot return the
    /// requested number of bytes, then it will return the result of
    /// invoking an optional callback function (supplied at construction);
    /// if no callback function was specified, throw a `std::bad_alloc`
    /// exception in an exception-enabled build, or else abort the program
    /// in a non-exception build.  The behavior is undefined unless
    /// `0 <= size`.  Note that the alignment of the address returned
    /// depends on the alignment strategy (`MAXIMUM_ALIGNMENT` or
    /// `NATURAL_ALIGNMENT`) specified at construction of this buffer
    /// allocator.  Also note that it is up to the callback function whether
    /// to return zero or throw an exception if this buffer allocator is
    /// unable to satisfy the request.
    void *allocate(size_type size) BSLS_KEYWORD_OVERRIDE;

    /// This function has no effect for this buffer allocator.
    void deallocate(void *address) BSLS_KEYWORD_OVERRIDE;

    // ACCESSORS

    /// Write formatted debugging information to `stdout` describing the
    /// internal state of this buffer allocator.
    void print() const;
};

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

                        // ---------------------
                        // class BufferAllocator
                        // ---------------------

// CREATORS
inline
BufferAllocator::BufferAllocator(char          *buffer,
                                 size_type      bufSize,
                                 AllocCallback  allocCallback)
: d_strategy(MAXIMUM_ALIGNMENT)
, d_cursor(0)
, d_buffer_p(buffer)
, d_bufferSize(static_cast<int>(bufSize))
, d_allocCallback(allocCallback)
{
}

inline
BufferAllocator::BufferAllocator(char              *buffer,
                                 size_type          bufSize,
                                 AlignmentStrategy  strategy,
                                 AllocCallback      allocCallback)
: d_strategy(strategy)
, d_cursor(0)
, d_buffer_p(buffer)
, d_bufferSize(static_cast<int>(bufSize))
, d_allocCallback(allocCallback)
{
}

// MANIPULATORS
inline
void BufferAllocator::deallocate(void *)
{
}

}  // close package namespace

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

/// This alias is defined for backward compatibility.
typedef bslma::BufferAllocator bslma_BufferAllocator;
#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 ----------------------------------
