// bslma_autodeallocator.h                                            -*-C++-*-
#ifndef INCLUDED_BSLMA_AUTODEALLOCATOR
#define INCLUDED_BSLMA_AUTODEALLOCATOR

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

//@PURPOSE: Provide a range proctor to managed a block of memory.
//
//@CLASSES:
//  bslma::AutoDeallocator: range proctor to manage a block of memory
//
//@SEE_ALSO: bslma_deallocatorguard, bslma_deallocatorproctor
//
//@DESCRIPTION: This component provides a range proctor class template,
// 'bslma::AutoDeallocator', to manage a sequence of blocks of
// (otherwise-unmanaged) memory of parameterized 'TYPE' supplied at
// construction.  If not explicitly released, the sequence of managed memory
// blocks are deallocated automatically when the range proctor goes out of
// scope by freeing the memory using the parameterized 'ALLOCATOR' (allocator
// or pool) supplied at construction.  Note that after a range proctor releases
// its managed sequence of memory, the same proctor can be reused to
// conditionally manage another sequence of memory (allocated from the same
// allocator or pool that was supplied at construction) by invoking the 'reset'
// method.
//
///Requirement
///-----------
// The parameterized 'ALLOCATOR' type of the 'bslma::AutoRawDeleter' class
// template must provide a (possibly 'virtual') method:
//..
//  void deallocate(void *address);
//..
// to deallocate memory at the specified 'address' (originally supplied by the
// 'ALLOCATOR' object).
//
///Usage
///-----
// The 'bslma::AutoDeallocator' proctor object can be used to achieve
// *exception* *safety* in an *exception* *neutral* way during manipulation of
// "out-of-place" arrays of raw resources or memory.  Since there are no
// destructor calls, this component is more efficient compared to the
// 'bslma::AutoRawDeleter'.  The following illustrates the insertion operation
// for an "out-of-place" array of raw character sequences.  Assume that an
// array initially contains 5 character sequences as its elements:
//..
//     0     1     2     3     4
//   _____ _____ _____ _____ _____
//  |  o  |  o  |  o  |  o  |  o  |
//  `==|==^==|==^==|==^==|==^==|=='
//     |    _V___  |   __V___  |
//     |   |"Bye"| |  |"berg"| |
//     |   `=====' |  `======' |
//    _V_____     _V_____     _V__
//   |"Hello"|   |"Bloom"|   |"LP"|
//   `======='   `======='   `===='
//..
// To insert two more character sequences at index position 2, the array is
// first reallocated if it is not big enough, and then the existing elements at
// index positions 2, 3, and 4 are shifted:
//..
//     0     1     2     3     4     5     6
//   _____ _____ _____ _____ _____ _____ _____
//  |  o  |  o  |xxxxx|xxxxx|  o  |  o  |  o  |
//  `==|==^==|==^=====^=====^==|==^==|==^==|=='
//     |    _V___              |   __V___  |
//     |   |"Bye"|             |  |"berg"| |
//     |   `====='             |  `======' |
//    _V_____                 _V_____     _V__
//   |"Hello"|               |"Bloom"|   |"LP"|
//   `======='               `======='   `===='
//
//  Note: "xxxxx" denotes undefined value.
//..
// Next, two new memory blocks must be allocated to position 2 and 3.  If, one
// of the two allocations fails and an exception is thrown, the array will be
// left in an invalid state because the addresses contained at index positions
// 2 and 3 may be duplicates of those at index positions 4 and 5, or, if a
// resize occurred, invalid altogether.  We can restore exception neutrality by
// setting the array's length to 2 before attempting to create the string
// objects, but there is still a problem: the character sequences "Bloom",
// "berg", and "LP" (at index positions 4, 5, and 6) are "orphaned" and will
// never be deallocated -- a memory leak.  To prevent this potential memory
// leak, we can additionally create a 'bslma::AutoDeallocator' object to manage
// (temporarily) the memory at index positions 4, 5, and 6 prior to allocating
// the new memory:
//..
//      0     1     2     3     4     5     6
//    _____ _____ _____ _____ _____ _____ _____
//   |  o  |  o  |xxxxx|xxxxx|  o  |  o  |  o  |
//   `==|==^==|==^=====^=====^==|==^==|==^==|=='
//      |    _V___              |   __V___  |
//      |   |"Bye"|             |  |"berg"| |
//      |   `====='             |  `======' |
//     _V_____                 _V_____     _V__
//    |"Hello"|               |"Bloom"|   |"LP"|
//    `======='               `======='   `===='
//   my_StrArray              ^---------------bslma::AutoDeallocator
//   (length = 2)                          (origin = 4, length = 3)
//
//  Note: Configuration after initializing the proctor.
//..
// If an exception occurs, the array (now of length 2) is in a perfectly valid
// state, while the proctor is responsible for deallocating the orphaned memory
// at index positions 4, 5, and 6.  If no exception is thrown, the length is
// set to 7 and the proctor's 'release' method is called, releasing its control
// over the (temporarily) managed memory.
//
// The following example illustrates the use of 'bslma::AutoDeallocator' to
// manage temporarily an "out-of-place" array of character sequences during the
// array's insertion operation.
//
// First we define a 'my_StrArray' class that stores an array of character
// sequences.
//..
//  // my_strarray.h
//  // ...
//
//  template <class ALLOCATOR>
//  class my_StrArray {
//      // This class is a container that stores an array of character
//      // sequences.  Memory will be supplied by the parameterized 'ALLOCATOR'
//      // type provided at construction (which must remain valid throughout
//      // the lifetime of this guard object).  Note that memory is managed by
//      // a parameterized 'ALLOCATOR' type, instead of a 'bslma::Allocator',
//      // to enable clients to pass in a pool (such as a sequential pool)
//      // optimized for allocations of character sequences.
//
//      // DATA
//      char      **d_array_p;      // dynamically allocated array of character
//                                  // sequence
//
//      int         d_length;       // logical length of this array
//
//      int         d_size;         // physical capacity of this array
//
//      ALLOCATOR  *d_allocator_p;  // allocator or pool (held, not owned)
//
//    public:
//      // CREATORS
//      my_StrArray(ALLOCATOR *basicAllocator);
//          // Create a 'my_StrArray' object using the specified
//          // 'basicAllocator' used to supply memory.
//
//      ~my_StrArray();
//          // Destroy this 'my_StrArray' object and all elements currently
//          // stored.
//
//      // MANIPULATORS
//      void append(const char *src);
//          // Append to this array the string 'src'.
//
//      void insert(int dstIndex, const my_StrArray& srcArray);
//          // Insert into this array at the specified 'dstIndex', the
//          // character sequences in the specified 'srcArray'.  All values
//          // with initial indices at or above 'dstIndex' are shifted up by
//          // unless '0 <= dstIndex' and 'dstIndex <= length()'.  Note that
//          // this method is functionally the same as 'insert2', but has a
//          // different implementation to facilitate the usage example.
//
//      void insert2(int dstIndex, const my_StrArray& srcArray);
//          // Insert into this array at the specified 'dstIndex', the
//          // character sequences in the specified 'srcArray'.  All values
//          // with initial indices at or above 'dstIndex' are shifted up by
//          // unless '0 <= dstIndex' and 'dstIndex <= length()'.  Note that
//          // this method is functionally the same as 'insert', but has a
//          // different implementation to facilitate the usage example.
//
//      // ...
//
//      // ACCESSORS
//      int length() const;
//          // Return the logical length of this array.
//
//      const char *operator[](int index) const;
//          // Return a pointer to the 'index'th string in the array.  Note
//          // the pointer is returned by value, it is not a reference to a
//          // pointer.
//
//      // ...
//  };
//..
// Next, we define the 'insert' method of 'my_StrArray':
//..
//  template <class ALLOCATOR>
//  void my_StrArray<ALLOCATOR>::insert(int                           dstIndex,
//                                      const my_StrArray<ALLOCATOR>& srcArray)
//  {
//      int srcLength  = srcArray.d_length;
//      int newLength  = d_length + srcLength;
//      int numShifted = d_length - dstIndex;
//
//      if (newLength > d_size) {
//          while (d_size < newLength) {
//              d_size = ! d_size ? 1 : 2 * d_size;
//          }
//
//          char ** newArray =
//                  (char **) d_allocator_p->allocate(d_size * sizeof(char *));
//          memcpy(newArray, d_array_p, d_length * sizeof(char *));
//          if (d_array_p) {
//              d_allocator_p->deallocate(d_array_p);
//          }
//          d_array_p = newArray;
//      }
//
//      char **tmpSrc = srcArray.d_array_p;
//      if (this == &srcArray) {
//          // self-alias
//          size_t size = srcLength * sizeof(char *);
//          tmpSrc = (char **) d_allocator_p->allocate(size);
//          memcpy(tmpSrc, d_array_p, size);
//      }
//      bslma::DeallocatorProctor<ALLOCATOR> proctor(
//                                              this == &srcArray ? tmpSrc : 0,
//                                              d_allocator_p);
//
//      // First shift the elements to the back of the array.
//      memmove(d_array_p + dstIndex + srcLength,
//              d_array_p + dstIndex,
//              numShifted * sizeof *d_array_p);
//
//      // Shorten 'd_length' and use 'bslma::AutoDeallocator' to proctor the
//      // memory shifted.
//      d_length = dstIndex;
//
//      //*******************************************************
//      // Note use of auto deallocator on tail memory (below). *
//      //*******************************************************
//
//      bslma::AutoDeallocator<ALLOCATOR> tailDeallocator(
//                                  (void **) d_array_p + dstIndex + srcLength,
//                                  d_allocator_p,
//                                  numShifted);
//..
// Now, if any allocation for the inserted character sequences throws, the
// memory used for the character sequences that had been moved to the end of
// array will be deallocated automatically by the 'bslma::AutoDeallocator'.
//..
//      // Copy the character sequences from the 'srcArray'.
//      for (int i = 0; i < srcLength; ++i, ++d_length) {
//          std::size_t size = std::strlen(tmpSrc[i]) + 1;
//          d_array_p[dstIndex + i] = (char *) d_allocator_p->allocate(size);
//          memcpy(d_array_p[dstIndex + i], tmpSrc[i], size);
//      }
//
//      //*********************************************
//      // Note that the proctor is released (below). *
//      //*********************************************
//
//      tailDeallocator.release();
//      d_length = newLength;
//  }
//..
// The above method copies the source elements (visually) from left to right.
// Another (functionally equivalent) implementation copies the source elements
// from right to left, and makes use of the 'operator--()' of the
// 'bslma::AutoDeallocator' interface:
//..
//  template <class ALLOCATOR>
//  void my_StrArray<ALLOCATOR>::insert2(int                          dstIndex,
//                                      const my_StrArray<ALLOCATOR>& srcArray)
//  {
//      int srcLength  = srcArray.d_length;
//      int newLength  = d_length + srcLength;
//      int numShifted = d_length - dstIndex;
//
//      if (newLength > d_size) {
//          while (d_size < newLength) {
//              d_size = ! d_size ? 1 : 2 * d_size;
//          }
//
//          char ** newArray =
//                  (char **) d_allocator_p->allocate(d_size * sizeof(char *));
//          memcpy(newArray, d_array_p, d_length * sizeof(char *));
//          if (d_array_p) {
//              d_allocator_p->deallocate(d_array_p);
//          }
//          d_array_p = newArray;
//      }
//
//      char **tmpSrc = srcArray.d_array_p;
//      if (this == &srcArray) {
//          // self-alias
//          size_t size = srcLength * sizeof(char *);
//          tmpSrc = (char **) d_allocator_p->allocate(size);
//          memcpy(tmpSrc, d_array_p, size);
//      }
//      bslma::DeallocatorProctor<ALLOCATOR> proctor(
//                                              this == &srcArray ? tmpSrc : 0,
//                                              d_allocator_p);
//
//      // First shift the elements to the back of the array.
//      memmove(d_array_p + dstIndex + srcLength,
//              d_array_p + dstIndex,
//              numShifted * sizeof *d_array_p);
//
//      // Shorten 'd_length' and use 'bslma::AutoDeallocator' to proctor the
//      // memory shifted.
//      d_length = dstIndex;
//
//      //********************************************
//      //* Note the use of auto deallocator on tail *
//      //* memory with negative length (below).     *
//      //********************************************
//
//      bslma::AutoDeallocator<ALLOCATOR> tailDeallocator(
//                     (void **) d_array_p + d_length + srcLength + numShifted,
//                     d_allocator_p,
//                     -numShifted);
//..
// Since we have decided to copy the source elements from right to left, we set
// the origin of the 'bslma::AutoDeallocator' to the end of the array, and
// decrement the (signed) length on each copy to extend the proctor range by 1.
//..
//      // Copy the character sequences from the 'srcArray'.  Note that the
//      // 'tailDeallocator' has to be decremented to cover the newly
//      // created object.
//
//      for (int i = srcLength - 1; i >= 0; --i, --tailDeallocator) {
//          std::size_t size = std::strlen(tmpSrc[i]) + 1;
//          d_array_p[dstIndex + i] = (char *)d_allocator_p->allocate(size);
//          memcpy(d_array_p[dstIndex + i], tmpSrc[i], size);
//      }
//
//      //*********************************************
//      // Note that the proctor is released (below). *
//      //*********************************************
//
//      tailDeallocator.release();
//      d_length = newLength;
//  }
//..
// Note that though the two implementations are functionally equivalent, they
// are logically different.  First of all, the second implementation will be
// slightly slower because it is accessing memory backwards when compared to
// the normal forward sequential access.  Secondly, in case of an exception,
// the first implementation will retain all the elements copied prior to the
// exception, whereas the second implementation will remove them.

#include <bslscm_version.h>

#include <bsls_assert.h>
#include <bsls_performancehint.h>

namespace BloombergLP {

namespace bslma {

                        // =====================
                        // class AutoDeallocator
                        // =====================

template <class ALLOCATOR>
class AutoDeallocator {
    // This class implements a range proctor that, unless its 'release' method
    // has previously been invoked, automatically deallocates the contiguous
    // sequence of managed memory blocks upon its own destruction by invoking
    // the 'deallocate' method of an allocator (or pool) of parameterized
    // 'ALLOCATOR' type supplied to it at construction.  Each of the managed
    // memory blocks must have been supplied by this allocator (or pool), which
    // must remain valid throughout the lifetime of the range proctor.  Note
    // that when the length of this object is non-zero, it must refer to a
    // non-null array of memory blocks.

    // DATA
    void      **d_origin_p;     // reference location for the sequence of
                                // managed memory

    int         d_length;       // number of memory blocks managed (sign
                                // encodes direction)

    ALLOCATOR  *d_allocator_p;  // allocator or pool (held, not owned)

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

  private:
    // PRIVATE MANIPULATORS
    void deallocate();
        // Deallocate the contiguous sequence of memory blocks managed by this
        // auto deallocator (if any) by invoking the 'deallocate' method of the
        // allocator (or pool) supplied at construction on each memory block.
        // Note that the order in which the managed memory blocks are
        // deallocated is undefined.  Also note that this method factors out
        // the deallocation logic, which allows the destructor to be declared
        // 'inline' for the common case (the range proctor is released before
        // being destroyed).

  public:
    // CREATORS
    template <class TYPE>
    AutoDeallocator(TYPE      **origin,
                    ALLOCATOR  *allocator,
                    int         length = 0);
    AutoDeallocator(void      **origin,
                    ALLOCATOR  *allocator,
                    int         length = 0);
        // Create an auto deallocator to manage a sequence of memory blocks at
        // the specified 'origin', and that uses the specified 'allocator' to
        // deallocate the sequence of memory blocks managed by this range
        // proctor (if not released -- see 'release') upon destruction.
        // Optionally specify 'length' to define its range, which by default is
        // empty (i.e., 'length = 0').  The sequence of memory blocks may
        // extend in either direction from 'origin'.  A positive 'length'
        // represents the sequence of memory blocks starting at 'origin' and
        // extending "up" to 'length' (*not* including the memory block at the
        // index position 'origin + length').  A negative 'length' represents
        // the sequence of memory blocks starting at one position below
        // 'origin' and extending "down" to the absolute value of 'length'
        // (including the memory block at index position 'origin + length').
        // If 'length' is 0, then this range proctor manages no memory blocks.
        // If 'origin' is non-zero, all memory blocks within the proctored
        // range (if any) must be supplied by 'allocator'.  The behavior is
        // undefined unless 'allocator' is non-zero, and, if 'origin' is 0,
        // 'length' is also 0.  Note that when 'length' is non-positive, the
        // memory block at the origin is *not* managed by this range proctor.
        // For example, if 'origin' is at the index position 2, a 'length' of 2
        // signifies that the memory blocks at positions 2 and 3 are managed,
        // whereas a 'length' of -2 signifies that the memory blocks at
        // positions 0 and 1 are managed:
        //..
        //     length = -2                            length = 2
        //     |<----->|                              |<----->|
        //      ___ ___ ___ ___ ___            ___ ___ ___ ___ ___
        //     | 0 | 1 | 2 | 3 | 4 |          | 0 | 1 | 2 | 3 | 4 |
        //     `===^===^===^===^==='          `===^===^===^===^==='
        //             ^------------ origin           ^------------ origin
        //..

    ~AutoDeallocator();
        // Destroy this range proctor and deallocate the contiguous sequence of
        // memory blocks it manages (if any) by invoking the 'deallocate'
        // method of the allocator (or pool) supplied at construction on each
        // memory block.  Note that the order in which the managed memory
        // blocks are deallocated is undefined.

    // MANIPULATORS
    void operator++();
        // Increase by one the (signed) length of the sequence of memory blocks
        // managed by this range proctor.  The behavior is undefined unless the
        // origin of the sequence of memory blocks managed by this proctor is
        // non-zero.  The behavior is undefined unless the origin or this range
        // proctor is non-zero.  Note that if the length of this proctor is
        // currently negative, the number of managed memory blocks will
        // decrease by one, whereas if the length is non-negative, the number
        // of managed memory blocks will increase by one.

    void operator--();
        // Decrease by one the (signed) length of the sequence of memory blocks
        // managed by this range proctor.  The behavior is undefined unless the
        // origin of the sequence of memory blocks managed by this proctor is
        // non-zero.  The behavior is undefined unless the origin or this range
        // proctor is non-zero.  Note that if the length of this proctor is
        // currently positive, the number of managed memory blocks will
        // decrease by one, whereas if the length is non-positive, the number
        // of managed memory blocks will increase by one.

    void release();
        // Release from management the sequence of memory blocks currently
        // managed by this range proctor by setting the length of the managed
        // sequence to 0.  All memory blocks currently under management will
        // become unmanaged (i.e., when the proctor goes out of scope and it
        // was not assigned another sequence of memory blocks to manage by
        // invoking 'reset', no memory blocks will be deallocated).  If no
        // memory blocks are currently being managed, this method has no
        // effect.  Note that the origin is not affected.

    template <class TYPE>
    void reset(TYPE **origin);
    void reset(void **origin);
        // Set the specified 'origin' as the origin of the sequence of memory
        // blocks to be managed by this range proctor.  The behavior is
        // undefined unless 'origin' is non-zero.  Note that the length of the
        // sequence of memory blocks managed by this proctor is not affected,
        // and 'setLength' should be invoked if the managed range is different
        // from the previously managed sequence of memory blocks.  Also note
        // that this method releases any previously-managed memory blocks from
        // management (without deallocating them), and so may be called with or
        // without having called 'release' when reusing this object.

    void setLength(int length);
        // Set the (signed) length of the sequence of memory blocks managed by
        // this range proctor to the specified 'length'.  The behavior is
        // undefined unless the origin of this range proctor is non-zero.

    // ACCESSORS
    int length() const;
        // Return the (signed) length of the sequence of memory blocks managed
        // by this proctor.
};

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

                        // ---------------------
                        // class AutoDeallocator
                        // ---------------------

// PRIVATE MANIPULATORS
template <class ALLOCATOR>
void AutoDeallocator<ALLOCATOR>::deallocate()
{
    if (d_length > 0) {
        for (; d_length > 0; --d_length, ++d_origin_p) {
            d_allocator_p->deallocate(*d_origin_p);
        }
    }
    else {
        --d_origin_p;
        for (; d_length < 0; ++d_length, --d_origin_p) {
            d_allocator_p->deallocate(*d_origin_p);
        }
    }
}

// CREATORS
template <class ALLOCATOR>
template <class TYPE>
inline
AutoDeallocator<ALLOCATOR>
::AutoDeallocator(TYPE      **origin,
                  ALLOCATOR  *allocator,
                  int         length)
: d_origin_p((void **)origin)
, d_length(length)
, d_allocator_p(allocator)
{
    BSLS_ASSERT_SAFE(allocator);
    BSLS_ASSERT_SAFE(origin || !length);
}

template <class ALLOCATOR>
inline
AutoDeallocator<ALLOCATOR>
::AutoDeallocator(void      **origin,
                  ALLOCATOR  *allocator,
                  int         length)
: d_origin_p(origin)
, d_length(length)
, d_allocator_p(allocator)
{
    BSLS_ASSERT_SAFE(allocator);
    BSLS_ASSERT_SAFE(origin || !length);
}

template <class ALLOCATOR>
inline
AutoDeallocator<ALLOCATOR>::~AutoDeallocator()
{
    BSLS_ASSERT_SAFE(d_origin_p || !d_length);

    if (BSLS_PERFORMANCEHINT_PREDICT_UNLIKELY(d_length)) {
        deallocate();
    }
}

// MANIPULATORS
template <class ALLOCATOR>
inline
void AutoDeallocator<ALLOCATOR>::operator++()
{
    BSLS_ASSERT_SAFE(d_origin_p);

    ++d_length;
}

template <class ALLOCATOR>
inline
void AutoDeallocator<ALLOCATOR>::operator--()
{
    BSLS_ASSERT_SAFE(d_origin_p);

    --d_length;
}

template <class ALLOCATOR>
inline
void AutoDeallocator<ALLOCATOR>::release()
{
    d_length = 0;
}

template <class ALLOCATOR>
template <class TYPE>
inline
void AutoDeallocator<ALLOCATOR>::reset(TYPE **origin)
{
    BSLS_ASSERT_SAFE(origin);

    d_origin_p = static_cast<void **>(origin);
}

template <class ALLOCATOR>
inline
void AutoDeallocator<ALLOCATOR>::reset(void **origin)
{
    BSLS_ASSERT_SAFE(origin);

    d_origin_p = origin;
}

template <class ALLOCATOR>
inline
void
AutoDeallocator<ALLOCATOR>::setLength(int length)
{
    BSLS_ASSERT_SAFE(d_origin_p);

    d_length = length;
}

// ACCESSORS
template <class ALLOCATOR>
inline
int AutoDeallocator<ALLOCATOR>::length() const
{
    return d_length;
}

}  // close package namespace

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

#ifdef bslma_AutoDeallocator
#undef bslma_AutoDeallocator
#endif
#define bslma_AutoDeallocator bslma::AutoDeallocator
    // This alias is defined for backward compatibility.
#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 ----------------------------------