// bslalg_autoarraymovedestructor.h                                   -*-C++-*-
#ifndef INCLUDED_BSLALG_AUTOARRAYMOVEDESTRUCTOR
#define INCLUDED_BSLALG_AUTOARRAYMOVEDESTRUCTOR

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

//@PURPOSE: Provide a proctor for destroying arrays.
//
//@CLASSES:
//  bslalg::AutoArrayMoveDestructor: exception-neutrality guard for arrays
//
//@SEE_ALSO: bslma_autodestructor, bslalg_autoarraydestructor
//
//@DESCRIPTION: This component provides a proctor object to manage a contiguous
// (in-place) sequence of otherwise-unmanaged instances of a user-defined type.
// If not explicitly released, all objects managed by the proctor object are
// automatically destroyed by the proctor's destructor or moved back to their
// original area, using the 'bslalg_arraydestructionprimitives' and
// 'std::memmove'.  This component is intended to be used only with bit-wise
// moveable types, and for a very special purpose as shown in the usage
// example.
//
// Overview of the operation of 'AutoArrayMoveDestructor':
// ----------------------------------------------------------------------------
// Suppose we want to double the length of an array by prepending copies a
// 'value' at the start of the array.  Note that we assume there is ample
// uninitialized memory after the end of the initial array for these new
// values to be inserted.
//
// Legend:
//..
//    'ABCDE'   -- initial array elements.
//    'v'       -- copy of specified 'value' being inserted.
//    '.'       -- (period) uninitialized memory.
//    '^(,)'    -- area guarded by 'AutoArrayMoveDestructor', where:
//                 '^' -- position of 'guard.destination()'
//                 '(' -- position of 'guard.begin()'
//                 ',' -- (comma) position of 'guard.middle()'
//                 ')' -- position of 'guard.end()'
//..
// The copy constructor for the type being inserted may throw, so we need to
// have a guard object that allows us to make some guarantee about the state of
// the array after the guard is destroyed.  What we want to guarantee is that
// there are as many valid objects at the start of the array as before with no
// other valid objects in existence.
//
// The following steps show a successful operation prepending copies of the
// value 'v':
//..
//  1: 'ABCDE.....'      -- initial memory.
//  2: '.....ABCDE'      -- memory after first 'std::memcpy'.
//  3: '^.....(,ABCDE)'  -- memory immediately after 'guard' is set
//  4: 'vv^...(AB,CDE)'  -- memory after 2 copies of 'value' have been
//                          created, and 'guard.advance()' has been called
//                          twice.
//  5: 'vvvvv^(ABCDE,)'  -- memory after insertion is completed
//  6: 'vvvvvABCDE'      -- memory after guard was destroyed (at which point
//                          'guard.middle() == guard.end()' so destructor did
//                          nothing.
//..
// Now suppose we threw after step 4, destroying 'guard'.
//..
//  4:  'vv^...(AB,CDE)' -- same as step '4' above, before destructor starts
//  5b: 'vv^CDE(AB,...)' -- memory after 'guard's destructor moves 'CDE' back
//                          to their position before we began
//  6b: 'vv^CDE(..,...)' -- memory after 'guard's destructor destroys 'A' and
//                          'B'
//  7b: 'vvCDE.....'     -- memory after 'guard's destructor completes
//..
// We now have 5 valid elements in the beginning of the range, as it was when
// we started, making the situation predictable for our next destructor.
//
// This was a very simple case, but using this guard in conjunction with
// 'bslalg::AutoArrayDestructor', we can implement the more general case of
// inserting arbitrary numbers of elements at the beginning of an array.
//
///Usage
///-----
// In this section we show intended use of this component.
//
///Example 1: Doubling the Length of an Array
/// - - - - - - - - - - - - - - - - - - - - -
// First, we create the class 'TestType', which is bitwise-movable and
// allocates memory upon construction:
//..
//                             // ==============
//                             // class TestType
//                             // ==============
//
//  class TestType {
//      // This test type contains a 'char' in some allocated storage.  It
//      // counts the number of default and copy constructions, assignments,
//      // and destructions.  It has no traits other than using a 'bslma'
//      // allocator.  It could have the bit-wise moveable traits but we defer
//      // that trait to the 'MoveableTestType'.
//
//      char             *d_data_p;
//      bslma::Allocator *d_allocator_p;
//
//    public:
//      // CREATORS
//      explicit TestType(bslma::Allocator *basicAllocator = 0)
//      : d_data_p(0)
//      , d_allocator_p(bslma::Default::allocator(basicAllocator))
//      {
//          ++numDefaultCtorCalls;
//          d_data_p  = (char *)d_allocator_p->allocate(sizeof(char));
//          *d_data_p = '?';
//      }
//
//      explicit TestType(char c, bslma::Allocator *basicAllocator = 0)
//      : d_data_p(0)
//      , d_allocator_p(bslma::Default::allocator(basicAllocator))
//      {
//          ++numCharCtorCalls;
//          d_data_p  = (char *)d_allocator_p->allocate(sizeof(char));
//          *d_data_p = c;
//      }
//
//      TestType(const TestType&   original,
//               bslma::Allocator *basicAllocator = 0)
//      : d_data_p(0)
//      , d_allocator_p(bslma::Default::allocator(basicAllocator))
//      {
//          ++numCopyCtorCalls;
//          if (&original != this) {
//              d_data_p  = (char *)d_allocator_p->allocate(sizeof(char));
//              *d_data_p = *original.d_data_p;
//          }
//      }
//
//      ~TestType()
//      {
//          ++numDestructorCalls;
//          *d_data_p = '_';
//          d_allocator_p->deallocate(d_data_p);
//          d_data_p = 0;
//      }
//
//      // MANIPULATORS
//      TestType& operator=(const TestType& rhs)
//      {
//          ++numAssignmentCalls;
//          if (&rhs != this) {
//              char *newData = (char *)d_allocator_p->allocate(sizeof(char));
//              *d_data_p = '_';
//              d_allocator_p->deallocate(d_data_p);
//              d_data_p  = newData;
//              *d_data_p = *rhs.d_data_p;
//          }
//          return *this;
//      }
//
//      void setDatum(char c) { *d_data_p = c; }
//
//      // ACCESSORS
//      char datum() const { return *d_data_p; }
//
//      void print() const
//      {
//          if (d_data_p) {
//              assert(isalpha(*d_data_p));
//              printf("%c (int: %d)\n", *d_data_p, (int)*d_data_p);
//          } else {
//              printf("VOID\n");
//          }
//      }
//  };
//
//  // FREE OPERATORS
//  bool operator==(const TestType& lhs, const TestType& rhs)
//  {
//      return lhs.datum() == rhs.datum();
//  }
//
//  // TRAITS
//  namespace BloombergLP {
//
//  namespace bslma {
//  template <> struct UsesBslmaAllocator<TestType> : bsl::true_type {};
//  }  // close namespace bslma
//
//  namespace bslmf {
//  template <> struct IsBitwiseMoveable<TestType> : bsl::true_type {};
//  }  // close namespace bslmf
//
//  }  // close enterprise namespace
//..
// Then, we define the function 'insertItems' which uses
// 'AutoArrayMoveDestructor' to ensure that if an exception is thrown (e.g.,
// when allocating memory), the array will be left in a state where it has the
// same number of elements, in the same location, as when the function begin
// (though not necessarily the same value).
//..
//  void insertItems(TestType         *start,
//                   TestType         *divider,
//                   const TestType    value,
//                   bslma::Allocator *allocator)
//      // The memory in the specified range '[ start, divider )' contains
//      // valid elements, and the range of valid elements is to be doubled by
//      // inserting 'divider - start' copies of the specified 'value' at
//      // 'start', shifting the existing valid values back in memory.  Assume
//      // that following the pointer 'divider' is sufficient uninitialized
//      // memory, and that the type 'TestType' is bitwise-movable
//      // ('AutoArrayMoveDestructor' will only work bitwise-movable types).
//  {
//      TestType *finish = divider + (divider - start);
//
//      BSLMF_ASSERT(bslmf::IsBitwiseMoveable< TestType>::value);
//      BSLMF_ASSERT(bslma::UsesBslmaAllocator<TestType>::value);
//
//      // The range '[ start, divider )' contains valid elements.  The range
//      // '[ divider, finish )' is of equal length and contains uninitialized
//      // memory.  We want to insert 'divider - start' copies of the specified
//      // 'value' at the front half of the range '[ start, finish )', moving
//      // the existing elements back to make room for them.  Note that the
//      // copy constructor of 'TestType' allocates memory and may throw, so we
//      // have to leave the array in a somewhat predictable state if we do
//      // throw.  What the bslalg::AutoArrayMoveDestructor will do is
//      // guarantee that, if it is destroyed before the insertion is complete,
//      // the range '[ start, divider )' will contain valid elements, and that
//      // no other valid elements will exist.
//      //
//      // Note that the existing elements, which are bitwise-moveable, may be
//      // *moved* about the container without the possibility of throwing an
//      // exception, but the newly inserted elements must be copy-constructed
//      // (requiring memory allocation).
//      //
//      // First, move the valid elements from '[ start, divider )' to
//      // '[ divider, finish )'.  This can be done without risk of a throw
//      // occurring.
//
//      std::memcpy((void *)divider,
//                  start,
//                  (divider - start) * sizeof(TestType));
//
//      typedef bslalg::AutoArrayMoveDestructor<TestType> Obj;
//      Obj guard(start, divider, divider, finish, allocator);
//
//      while (guard.middle() < guard.end()) {
//          // Call the copy constructor, which may throw.
//
//          new (guard.destination()) TestType(value, allocator);
//
//          // 'guard.advance()' increments 'guard.destination()' and
//          // 'guard.middle()' by one.
//
//          guard.advance();
//      }
//  }
//..
// Next, within the 'main' function of our task, we create our 'value' object,
// whose value will be 'v', to be inserted into the front of our range.
//..
//  TestType value('v');
//..
// Then, we create a test allocator, and use it to allocate memory for an array
// of 'TestType' objects:
//..
//  bslma::TestAllocator ta;
//
//  TestType *array = (TestType *) ta.allocate(10 * sizeof(TestType));
//..
// Next, we construct the first 5 elements of the array to have the values
// 'ABCDE'.
//..
//  TestType *p = array;
//  new (p++) TestType('A', &ta);
//  new (p++) TestType('B', &ta);
//  new (p++) TestType('C', &ta);
//  new (p++) TestType('D', &ta);
//  new (p++) TestType('E', &ta);
//..
// Then, we record the number of outstanding blocks in the allocator:
//..
//  const bsls::Types::Int64 N = ta.numBlocksInUse();
//..
// Next, we enter an 'exception test' block, which will repeatedly enter a
// block of code, catching exceptions throw by the test allocator 'ta' on each
// iteration:
//..
//  BSLMA_TESTALLOCATOR_EXCEPTION_TEST_BEGIN(ta)
//..
// Then, we observe that even if we've just caught an exception and re-entered
// the block, the amount of memory outstanding is unchanged from before we
// entered the block.
//..
//      assert(ta.numBlocksInUse() == N);
//..
// Note that when we threw, some of the values of the 5 elements of the array
// may have been changed to 'v', otherwise they will be unchanged.
//
// Next, we re-initialize those elements that have been overwritten in the last
// pass with 'value' to their values before we entered the block:
//..
//      if ('v' == array[0].datum()) array[0].setDatum('A');
//      if ('v' == array[1].datum()) array[1].setDatum('B');
//      if ('v' == array[2].datum()) array[2].setDatum('C');
//      if ('v' == array[3].datum()) array[3].setDatum('D');
//      if ('v' == array[4].datum()) array[4].setDatum('E');
//..
// Then, we call 'insertItems', which may throw:
//..
//      insertItems(array, p, value, &ta);
//..
// Next, we exit the except testing block.
//..
//  BSLMA_TESTALLOCATOR_EXCEPTION_TEST_END
//..
// Now, we verify that:
//: 1 we have allocated exactly 5 more blocks of memory, since each 'TestType'
//:   object allocates one block and 'insertItems' created 5 more 'TestType'
//:   objects.
//: 2 the values of the elements of the array are as expected.
//..
//  assert(ta.numBlocksInUse() == N + 5);
//
//  assert('v' == array[0].datum());
//  assert('v' == array[1].datum());
//  assert('v' == array[2].datum());
//  assert('v' == array[3].datum());
//  assert('v' == array[4].datum());
//  assert('A' == array[5].datum());
//  assert('B' == array[6].datum());
//  assert('C' == array[7].datum());
//  assert('D' == array[8].datum());
//  assert('E' == array[9].datum());
//..
// Finally, we destroy our array and check the allocator to verify no memory
// was leaked:
//..
//  for (int i = 0; i < 10; ++i) {
//      array[i].~TestType();
//  }
//  ta.deallocate(array);
//
//  assert(0 == ta.numBytesInUse());
//..

#include <bslscm_version.h>

#include <bslalg_arraydestructionprimitives.h>

#include <bslma_stdallocator.h>

#include <bslmf_assert.h>
#include <bslmf_isbitwisemoveable.h>

#include <bsls_assert.h>

#include <cstddef>        // 'std::size_t'
#include <cstring>        // 'memset', 'memcpy', 'memmove'

namespace BloombergLP {

namespace bslalg {

                    // =============================
                    // class AutoArrayMoveDestructor
                    // =============================

template <class OBJECT_TYPE, class ALLOCATOR = bsl::allocator<OBJECT_TYPE> >
class AutoArrayMoveDestructor {
    // This 'class' provides a specialized proctor object that, upon
    // destruction and unless the 'release' method has been called, bit-wise
    // moves the elements in a segment of an array of parameterized
    // 'OBJECT_TYPE' back to some destination, and destroys some other elements
    // in an adjacent segment of the same array.  The elements destroyed are
    // delimited by the range '[ begin(), middle() )' and those moved to
    // 'destination()' and in the range '[ middle(), end() )'.  Note that, once
    // constructed, 'begin()' and 'end()' remain fixed.  As the guard advances,
    // 'middle()' and 'destination()' move, reflecting the successful transfer
    // of data between the moving range and the destination.

    // DATA
    OBJECT_TYPE *d_dst_p;    // destination of the bit-wise move

    OBJECT_TYPE *d_begin_p;  // address of first element in guarded range

    OBJECT_TYPE *d_middle_p; // address of first moved element in guarded range
                             // that is also first address beyond last element
                             // destroyed in same guarded range

    OBJECT_TYPE *d_end_p;    // first address beyond last (moved) element in
                             // guarded range
    ALLOCATOR    d_allocator;  // allocator

    // CLASS INVARIANT
    BSLMF_ASSERT(bslmf::IsBitwiseMoveable<OBJECT_TYPE>::value);

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

  public:
    // CREATORS
    AutoArrayMoveDestructor(OBJECT_TYPE *destination,
                            OBJECT_TYPE *begin,
                            OBJECT_TYPE *middle,
                            OBJECT_TYPE *end,
                            ALLOCATOR    allocator = ALLOCATOR());
        // TBD: document the 'allocator' parameter.
        // Create a proctor for the sequence of elements of the parameterized
        // 'OBJECT_TYPE' in the specified range '[ begin, end )' which, upon
        // destruction, moves the range '[ begin, middle )' to the specified
        // 'destination' and destroys the '[ middle, end )' range.  The
        // behavior is undefined unless 'begin', 'middle', and 'end' refer to
        // a contiguous sequence of initialized 'OBJECT_TYPE' objects, where
        // 'begin <= middle <= end', and 'destination' refers to a contiguous
        // sequence of (uninitialized) memory of sufficient size to hold
        // 'end - begin' 'OBJECT_TYPE' objects (which must not overlap
        // '[begin, end)').

    ~AutoArrayMoveDestructor();
        // Bit-wise move the range '[ middle(), end() )' to the 'destination()'
        // address and destroy '[ begin(), middle() )'.

    // MANIPULATORS
    void advance();
        // Increment both middle and destination pointers by one position.  The
        // behavior is undefined if this operation result in 'destination()'
        // entering the '[ begin(), end() )' range.

    // ACCESSORS
    OBJECT_TYPE *begin() const;
        // Return the address at the beginning of the guarded range.

    OBJECT_TYPE *destination() const;
        // Return the destination address, to which the second portion of the
        // guarded range, delimited by '[ middle(), end() )', will be moved
        // upon destruction, or 0 if this guard has been released.

    OBJECT_TYPE *end() const;
        // Return the address at the end of the guarded range.

    OBJECT_TYPE *middle() const;
        // Return the address at the middle of the guarded range.
};

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

                    // -----------------------------
                    // class AutoArrayMoveDestructor
                    // -----------------------------

// CREATORS
template <class OBJECT_TYPE, class ALLOCATOR>
inline
AutoArrayMoveDestructor<OBJECT_TYPE, ALLOCATOR>::AutoArrayMoveDestructor(
                                                      OBJECT_TYPE *destination,
                                                      OBJECT_TYPE *begin,
                                                      OBJECT_TYPE *middle,
                                                      OBJECT_TYPE *end,
                                                      ALLOCATOR    allocator)
: d_dst_p(destination)
, d_begin_p(begin)
, d_middle_p(middle)
, d_end_p(end)
, d_allocator(allocator)
{
    BSLS_ASSERT_SAFE(!begin  == !middle);  // neither or both are null
    BSLS_ASSERT_SAFE(!middle == !end);     // neither or both are null
    BSLS_ASSERT_SAFE(destination || begin == middle);
    BSLS_ASSERT_SAFE(begin  <= middle);
    BSLS_ASSERT_SAFE(middle <= end);

}

template <class OBJECT_TYPE, class ALLOCATOR>
AutoArrayMoveDestructor<OBJECT_TYPE, ALLOCATOR>::~AutoArrayMoveDestructor()
{
    BSLS_ASSERT_SAFE(!d_begin_p  == !d_middle_p);  // neither or both are null
    BSLS_ASSERT_SAFE(!d_middle_p == !d_end_p);     // neither or both are null
    BSLS_ASSERT_SAFE(d_dst_p || d_begin_p == d_middle_p);
    BSLS_ASSERT_SAFE(d_begin_p  <= d_middle_p);
    BSLS_ASSERT_SAFE(d_middle_p <= d_end_p);
    BSLS_ASSERT_SAFE(d_dst_p    <  d_begin_p
                  || d_end_p    <= d_dst_p
                  || d_middle_p == d_end_p);

    if (d_middle_p != d_end_p) {
        std::size_t numBytes = (char *)d_end_p - (char *)d_middle_p;
        std::memcpy((void *)d_dst_p, d_middle_p, numBytes);
        ArrayDestructionPrimitives::destroy(d_begin_p,
                                            d_middle_p,
                                            d_allocator);
    }
}

// MANIPULATORS
template <class OBJECT_TYPE, class ALLOCATOR>
inline
void AutoArrayMoveDestructor<OBJECT_TYPE, ALLOCATOR>::advance()
{
    BSLS_ASSERT_SAFE(d_middle_p < d_end_p);

    ++d_middle_p;
    ++d_dst_p;

    BSLS_ASSERT_SAFE(d_dst_p != d_begin_p || d_middle_p == d_end_p);
}

// ACCESSORS
template <class OBJECT_TYPE, class ALLOCATOR>
inline
OBJECT_TYPE *AutoArrayMoveDestructor<OBJECT_TYPE, ALLOCATOR>::begin() const
{
    return d_begin_p;
}

template <class OBJECT_TYPE, class ALLOCATOR>
inline
OBJECT_TYPE *
AutoArrayMoveDestructor<OBJECT_TYPE, ALLOCATOR>::destination() const
{
    return d_dst_p;
}

template <class OBJECT_TYPE, class ALLOCATOR>
inline
OBJECT_TYPE *AutoArrayMoveDestructor<OBJECT_TYPE, ALLOCATOR>::end() const
{
    return d_end_p;
}

template <class OBJECT_TYPE, class ALLOCATOR>
inline
OBJECT_TYPE *AutoArrayMoveDestructor<OBJECT_TYPE, ALLOCATOR>::middle() const
{
    return d_middle_p;
}

}  // close package namespace

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

#ifdef bslalg_AutoArrayMoveDestructor
#undef bslalg_AutoArrayMoveDestructor
#endif
#define bslalg_AutoArrayMoveDestructor bslalg::AutoArrayMoveDestructor
    // 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 ----------------------------------