// bdlc_indexclerk.h                                                  -*-C++-*-

// ----------------------------------------------------------------------------
//                                   NOTICE
//
// This component is not up to date with current BDE coding standards, and
// should not be used as an example for new development.
// ----------------------------------------------------------------------------

#ifndef INCLUDED_BDLC_INDEXCLERK
#define INCLUDED_BDLC_INDEXCLERK

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

//@PURPOSE: Provide a manager of reusable, non-negative integer indices.
//
//@CLASSES:
//  bdlc::IndexClerkIter: sequential accessor to decommissioned indices
//  bdlc::IndexClerk: manager of reusable, non-negative integer indices
//
//@DESCRIPTION: This component implements an efficient, value-semantic manager
// class for reusable, non-negative integer indices.  Each new instance of a
// 'bdlc::IndexClerk' will issue consecutive integers on request, beginning
// with '0, 1, 2, ...'.  Indices that are no longer needed may be returned for
// reuse.  Existing decommissioned indices are reissued before any new ones are
// created.  Value-semantic operations such as copy construction and
// assignment, equality comparison, and streaming are also provided.  Finally,
// a 'bdlc::IndexClerkIter' is provided to enable sequential, read-only access
// to the currently decommissioned indices.  Note that the order of iteration
// is not defined.
//
///Performance
///-----------
// The following characterizes the performance of representative operations
// using "big-oh" notation, O[f(N,M)], where the names 'N' and 'M' also refer
// to the number of respective elements in the sequence of decommissioned
// indices.
//..
//     Operation                     Worst Case
//     ---------                     ----------
//     DEFAULT CTOR                  O[1]
//     COPY CTOR(N)                  O[N]
//     N.DTOR()                      O[1]
//     N.OP=(M)                      O[M]
//     OP==(N,M)                     O[min(N,M)]
//
//     N.getIndex()                  O[1]
//     N.putIndex(index)             O[1]
//     N.removeAll()                 O[1]
//     N.numCommissionedIndices()    O[1]
//     N.numDecommissionedIndices()  O[1]
//     N.nextNewIndex()              O[1]
//     N.isInUse(index)              O[N]
//..
//
///Usage
///-----
// A 'bdlc::IndexClerk' is commonly used in conjunction with an array to enable
// machine-address-independent referencing.  Rather than dynamically allocating
// an object and holding its address, the object is stored in the array at the
// next position dispensed by its associated 'bdlc::IndexClerk', and that index
// becomes an identifier (Id) for the new object.  Instead of destroying an
// unneeded object, its Id is merely returned to the clerk.
//
// Care must be taken to ensure that objects "created" at reused indices (i.e.,
// indices below the current length of the array) *replace* (the value of) an
// existing object in the array while objects created at new indices (i.e.,
// indices at the current length) are *appended* to the array.
//
// For example, suppose we have a security class object.  To add and remove
// security values from a security array/clerk pair, you might write the
// following two functions:
//..
//  int addSecurity(bsl::vector<Security> *securityArray,
//                  bdlc::IndexClerk      *securityClerk,
//                  const Security&        newSecurity)
//      // Add a copy of the specified 'newSecurity' to the specified
//      // 'securityArray' at the index dispensed by the specified
//      // 'securityClerk'.  Also update the 'securityClerk', and return the id
//      // (in 'securityArray') for the newly added security.
//  {
//      BSLS_ASSERT(securityArray);
//      BSLS_ASSERT(securityClerk);
//
//      int id = securityClerk->getIndex();
//
//      if (id < securityArray->size()) {
//          (*securityArray)[id] = newSecurity;
//      }
//      else {
//          securityArray->push_back(newSecurity);
//      }
//
//      return id;
//  }
//
//  void removeSecurity(bsl::vector<Security> *securityArray,
//                      bdlc::IndexClerk      *securityClerk,
//                      int                    securityId)
//      // Remove the security object identified by the specified 'securityId'
//      // from the specified 'securityArray', and update the specified
//      // 'securityClerk' (making 'securityId' available for reuse).  The
//      // behavior is undefined unless 'securityId' refers to an active
//      // security in 'securityArray' dispensed by 'securityClerk'.
//  {
//      BSLS_ASSERT(securityArray);
//      BSLS_ASSERT(securityClerk);
//
//      BSLS_ASSERT(0                             <= securityId);
//      BSLS_ASSERT(securityClerk->nextNewIndex() >  securityId);
//      BSLS_ASSERT(securityArray->size()         >  securityId);
//
//      // Note that the 'isInUse' function (below) runs in linear time.
//
//      BSLS_ASSERT_SAFE(securityClerk->isInUse(securityId));
//
//      (*securityArray)[securityId] = Security();  // optional
//      securityClerk->putIndex(securityId);
//  }
//..

#include <bdlscm_version.h>

#include <bslx_instreamfunctions.h>
#include <bslx_outstreamfunctions.h>

#include <bslma_allocator.h>
#include <bslma_usesbslmaallocator.h>

#include <bslmf_nestedtraitdeclaration.h>

#include <bsls_assert.h>
#include <bsls_review.h>

#include <bsl_iosfwd.h>
#include <bsl_iterator.h>
#include <bsl_vector.h>

#ifndef BDE_DONT_ALLOW_TRANSITIVE_INCLUDES
#include <bslalg_typetraits.h>
#endif // BDE_DONT_ALLOW_TRANSITIVE_INCLUDES

namespace BloombergLP {
namespace bdlc {

                            // ====================
                            // class IndexClerkIter
                            // ====================

class IndexClerkIter {
    // This class defines an in-core value-semantic iterator providing
    // sequential read-only access to the decommissioned indices of a
    // 'IndexClerk'.  The order of iteration is implementation dependent.

    // DATA
    bsl::reverse_iterator<const int *> d_index_p;  // pointer to current
                                                   // decommissioned index

    // FRIENDS
    friend bool operator==(const IndexClerkIter& lhs,
                           const IndexClerkIter& rhs);
    friend bool operator!=(const IndexClerkIter& lhs,
                           const IndexClerkIter& rhs);
  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(IndexClerkIter, bsl::is_trivially_copyable);

    // CREATORS
    IndexClerkIter();
        // Create an unbound iterator.

    IndexClerkIter(const int *index);
        // Create an iterator referring to the specified integer 'index'.

    IndexClerkIter(const IndexClerkIter& original);
        // Create an iterator having the same value as the specified 'original'
        // iterator.

  //~IndexClerkIter();
        // Destroy this index clerk iterator.  Note that this method is
        // generated by the compiler.

    // MANIPULATORS
    IndexClerkIter& operator=(const IndexClerkIter& rhs);
        // Create an iterator having the same value as the specified 'rhs'
        // iterator.

    IndexClerkIter& operator++();
        // Increment this iterator to refer to the next index in the
        // corresponding sequence of decommissioned indices.  Return a
        // reference to this modifiable iterator.  The behavior is undefined
        // unless the current index is within the range '[ begin() .. end() )'.

    IndexClerkIter& operator--();
        // Decrement this iterator to refer to the previous index in the
        // corresponding sequence of decommissioned indices.  Return a
        // reference to this modifiable iterator.  The behavior is undefined
        // unless the current index is within the range '( begin() .. end() ]'.

    // ACCESSORS
    int operator*() const;
        // Return the value of the integer to which this iterator currently
        // refers.  The behavior is undefined unless the iterator is within the
        // range '[ begin() .. end() )'.
};

bool operator==(const IndexClerkIter& lhs, const IndexClerkIter& rhs);
    // Return 'true' if 'lhs' and 'rhs' have the same value and 'false'
    // otherwise.  Two iterators have the same value if they refer to the same
    // element of the same container or if they both have the end iterator
    // value for the same container.  The behavior is undefined unless 'lhs'
    // and 'rhs' refer to the same container and are non-singular (i.e., are
    // not default-constructed or copies of singular iterators).

bool operator!=(const IndexClerkIter& lhs, const IndexClerkIter& rhs);
    // Return 'true' if 'lhs' and 'rhs' do not have the same value and 'false'
    // otherwise.  Two iterators do not have the same value if they do not
    // refer to the same element of the same container or if one has the end
    // iterator value of a container and the other refers to an element (not
    // the end) of the same container.  The behavior is undefined unless 'lhs'
    // and 'rhs' refer to the same container and are non-singular (i.e., are
    // not default-constructed or copies of singular iterators).

                              // ================
                              // class IndexClerk
                              // ================

class IndexClerk {
    // This class defines an efficient, value-semantic manager type for
    // reusable, non-negative integer indices.  The class invariants are that
    // the all decommissioned indices must be non-negative, less than the next
    // new index, and unique.

    // DATA
    bsl::vector<int> d_unusedStack;   // stack of decommissioned indices
    int              d_nextNewIndex;  // next unused index to be created

    // FRIENDS
    friend bool operator==(const IndexClerk&, const IndexClerk&);
    friend bool operator!=(const IndexClerk&, const IndexClerk&);

    // PRIVATE CLASS METHODS
    static bool areInvariantsPreserved(const bsl::vector<int>& unusedStack,
                                       int                     nextNewIndex);
        // Return 'true' if the class invariants of the object represented by
        // the specified 'unusedStack' are preserved and 'false' otherwise.
        // The class invariants are that all decommissioned indices are
        // non-negative, less than the specified 'nextNewIndex', and unique.
        // Note that the run time of this function is proportional to
        // 'numDecommissionedIndices()', but it requires temporary space that
        // is proportional to 'nextNewIndex'.

  public:
    // TRAITS
    BSLMF_NESTED_TRAIT_DECLARATION(IndexClerk, bslma::UsesBslmaAllocator);

    // CLASS METHODS
    static int maxSupportedBdexVersion(int versionSelector);
        // Return the maximum valid BDEX format version, as indicated by the
        // specified 'versionSelector', to be passed to the 'bdexStreamOut'
        // method.  Note that it is highly recommended that 'versionSelector'
        // be formatted as "YYYYMMDD", a date representation.  Also note that
        // 'versionSelector' should be a *compile*-time-chosen value that
        // selects a format version supported by both externalizer and
        // unexternalizer.  See the 'bslx' package-level documentation for more
        // information on BDEX streaming of value-semantic types and
        // containers.

    // CREATORS
    explicit IndexClerk(bslma::Allocator *basicAllocator = 0);
        // Create a new index clerk that dispenses consecutive non-negative
        // integers beginning with '0, 1, 2, ...'; however, indices returned
        // via 'putIndex' will be reissued before any new ones are created.
        // Optionally specify a 'basicAllocator' used to supply memory.  If
        // 'basicAllocator' is 0, the currently installed default allocator is
        // used.

    IndexClerk(const IndexClerk&  original,
               bslma::Allocator  *basicAllocator = 0);
        // Create a new index clerk having the value of the specified
        // 'original' index clerk.  Optionally specify a 'basicAllocator' used
        // to supply memory.  If 'basicAllocator' is 0, the currently installed
        // default allocator is used.

    ~IndexClerk();
        // Destroy this index clerk.

    // MANIPULATORS

    // !IndexClerk& operator=(const IndexClerk& rhs);
        // Assign to this index clerk the value of the specified 'rhs' index
        // clerk, and return a reference to this modifiable index clerk.  Note
        // that this method's definition is compiler generated.

    template <class STREAM>
    STREAM& bdexStreamIn(STREAM& stream, int version);
        // Assign to this object the value read from the specified input
        // 'stream' using the specified 'version' format, and return a
        // reference to 'stream'.  If 'stream' is initially invalid, this
        // operation has no effect.  If 'version' is not supported, this object
        // is unaltered and 'stream' is invalidated, but otherwise unmodified.
        // If 'version' is supported but 'stream' becomes invalid during this
        // operation, this object has an undefined, but valid, state.  Note
        // that no version is read from 'stream'.  See the 'bslx' package-level
        // documentation for more information on BDEX streaming of
        // value-semantic types and containers.

    int getIndex();
        // Return the next available unused integer index.  Existing
        // decommissioned indices are reissued before new ones are created.

    void putIndex(int index);
        // Return the specified 'index' to this index clerk, which indicates
        // that 'index' is no longer in use and may be reissued.  The behavior
        // is undefined if 'index' has never been generated by this clerk or is
        // currently decommissioned.

    void removeAll();
        // Remove all of the indices from this index clerk.  Note that the
        // following post conditions apply:
        //..
        //  assert(0 == numCommissionedIndices());
        //  assert(0 == numDecommissionedIndices());
        //  assert(0 == nextNewIndex());
        //..

    // ACCESSORS
    template <class STREAM>
    STREAM& bdexStreamOut(STREAM& stream, int version) const;
        // Write the value of this object, using the specified 'version'
        // format, to the specified output 'stream', and return a reference to
        // 'stream'.  If 'stream' is initially invalid, this operation has no
        // effect.  If 'version' is not supported, 'stream' is invalidated, but
        // otherwise unmodified.  Note that 'version' is not written to
        // 'stream'.  See the 'bslx' package-level documentation for more
        // information on BDEX streaming of value-semantic types and
        // containers.

    IndexClerkIter begin() const;
        // Return a 'IndexClerkIter' referring to the first index returned to
        // this 'IndexClerk' that is currently unused, or 'end()' if there are
        // currently no decommissioned indices.

    IndexClerkIter end() const;
        // Return a 'IndexClerkIter' referring to an invalid index, indicating
        // the end of the sequence of decommissioned index.

    bool isInUse(int index) const;
        // Return 'true' if the specified 'index' is currently in use, and
        // 'false' otherwise.  The behavior is undefined unless '0 <= index'
        // and 'index < nextNewIndex()'.  Note that this method runs in time
        // proportional to the number of decommissioned indices.

    int numCommissionedIndices() const;
        // Return the number of indices currently in use.

    int numDecommissionedIndices() const;
        // Return the number of indices that are currently decommissioned.

    int nextNewIndex() const;
        // Return the smallest (non-negative) index that has not been issued by
        // this index clerk.  Note that this function offers the client a
        // "peek" at the next "new" index, but has no effect on the value of
        // this index clerk.

    bsl::ostream& print(bsl::ostream& stream,
                        int           level = 0,
                        int           spacesPerLevel = 4) const;
        // Format this index clerk to the specified output 'stream' at the
        // (absolute value of) the optionally specified indentation 'level' and
        // return a reference to 'stream'.  If 'level' is specified, optionally
        // specify 'spacesPerLevel', the number of spaces per indentation level
        // for this and all of its nested objects.  If 'level' is negative,
        // suppress indentation of the first line.  If 'spacesPerLevel' is
        // negative, format the entire output on one line, suppressing all but
        // the initial indentation (as governed by 'level').  If 'stream' is
        // not valid on entry, this operation has no effect.

#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // pending deprecation

    static int maxSupportedBdexVersion();
        // !DEPRECATED!: Use 'maxSupportedBdexVersion(int)' instead.
        //
        // Return the most current BDEX streaming version number supported by
        // this class.

#endif // BDE_OMIT_INTERNAL_DEPRECATED -- pending deprecation
};

// FREE OPERATORS
bool operator==(const IndexClerk& lhs, const IndexClerk& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' index clerks have the
    // same value, and 'false' otherwise.  Two 'IndexClerk' objects have the
    // same value if they have the same 'nextNewIndex()' and would always
    // generate the same sequence of integer indices.

bool operator!=(const IndexClerk& lhs, const IndexClerk& rhs);
    // Return 'true' if the specified 'lhs' and 'rhs' index clerks do not have
    // the same value, and 'false' otherwise.  Two 'IndexClerk' objects do not
    // have the same value if they do not have the same 'nextNewIndex()', or
    // might generate different sequences of integer indices.

bsl::ostream& operator<<(bsl::ostream& stream, const IndexClerk& rhs);
    // Write the specified 'rhs' index clerk to the specified output 'stream'
    // in some single-line (human-readable) format, and return a reference to
    // the modifiable 'stream'.

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

                            // --------------------
                            // class IndexClerkIter
                            // --------------------

// CREATORS
inline
IndexClerkIter::IndexClerkIter()
: d_index_p(0)
{
}

inline
IndexClerkIter::IndexClerkIter(const int *index)
: d_index_p(index)
{
}

inline
IndexClerkIter::IndexClerkIter(const IndexClerkIter& original)
: d_index_p(original.d_index_p)
{
}

// MANIPULATORS
inline
IndexClerkIter&
IndexClerkIter::operator=(const IndexClerkIter& rhs)
{
    d_index_p = rhs.d_index_p;
    return *this;
}

inline
IndexClerkIter& IndexClerkIter::operator++()
{
    BSLS_ASSERT(0 != d_index_p.base());

    ++d_index_p;
    return *this;
}

inline
IndexClerkIter& IndexClerkIter::operator--()
{
    BSLS_ASSERT(0 != d_index_p.base());

    --d_index_p;
    return *this;
}

// ACCESSORS
inline
int IndexClerkIter::operator*() const
{
    BSLS_ASSERT(0 != d_index_p.base());

    return *d_index_p;
}

}  // close package namespace

// FREE OPERATORS
inline
bool bdlc::operator==(const IndexClerkIter& lhs, const IndexClerkIter& rhs)
{
    return lhs.d_index_p == rhs.d_index_p;
}

inline
bool bdlc::operator!=(const IndexClerkIter& lhs, const IndexClerkIter& rhs)
{
    return lhs.d_index_p != rhs.d_index_p;
}

namespace bdlc {

                              // ----------------
                              // class IndexClerk
                              // ----------------

// CREATORS
inline
IndexClerk::IndexClerk(bslma::Allocator *basicAllocator)
: d_unusedStack(basicAllocator)
, d_nextNewIndex(0)
{
}

inline
IndexClerk::IndexClerk(const IndexClerk&  original,
                       bslma::Allocator  *basicAllocator)
: d_unusedStack(original.d_unusedStack, basicAllocator)
, d_nextNewIndex(original.d_nextNewIndex)
{
}

inline
IndexClerk::~IndexClerk()
{
    BSLS_ASSERT_SAFE(areInvariantsPreserved(d_unusedStack, d_nextNewIndex));
}

// MANIPULATORS
inline
int IndexClerk::getIndex()
{
    if (d_unusedStack.empty()) {
        return d_nextNewIndex++;                                      // RETURN
    }
    else {
        int index = d_unusedStack.back();
        d_unusedStack.pop_back();
        return index;                                                 // RETURN
    }
}

inline
void IndexClerk::putIndex(int index)
{
    BSLS_ASSERT(0 <= index);
    BSLS_ASSERT(     index < d_nextNewIndex);
    BSLS_ASSERT_SAFE(isInUse(index));

    d_unusedStack.push_back(index);
}

inline
void IndexClerk::removeAll()
{
    d_unusedStack.clear();
    d_nextNewIndex = 0;
}

// Note: Order changed from declaration to make use of inlined 'removeAll'.

template <class STREAM>
STREAM& IndexClerk::bdexStreamIn(STREAM& stream, int version)
{
    switch (version) {
      case 1: {
        int nextNewIndex;
        stream.getInt32(nextNewIndex);

        if (!stream || nextNewIndex < 0) {
            stream.invalidate();
            return stream;                                            // RETURN
        }

        bsl::vector<int> unusedStack;
        bslx::InStreamFunctions::bdexStreamIn(stream, unusedStack, version);

        // Stream can be invalidated after streaming in 'd_unusedStack'.

        if (!stream || !areInvariantsPreserved(unusedStack, nextNewIndex)) {
            stream.invalidate();
            return stream;                                            // RETURN
        }

        d_unusedStack  = unusedStack;
        d_nextNewIndex = nextNewIndex;
      } break;
      default: {
        stream.invalidate();
      } break;
    }
    return stream;
}

// ACCESSORS
template <class STREAM>
inline
STREAM& IndexClerk::bdexStreamOut(STREAM& stream, int version) const
{
    if (stream) {
        switch (version) { // switch on the schema version
          case 1: {
            stream.putInt32(d_nextNewIndex);
            bslx::OutStreamFunctions::bdexStreamOut(
                                               stream, d_unusedStack, version);
          } break;
          default: {
            stream.invalidate();  // unrecognized version number
          }
        }
    }
    return stream;
}

inline
int IndexClerk::numCommissionedIndices() const
{
    return d_nextNewIndex - static_cast<int>(d_unusedStack.size());
}

inline
IndexClerkIter IndexClerk::begin() const
{
    return IndexClerkIter(d_unusedStack.begin() + d_unusedStack.size());
}

inline
IndexClerkIter IndexClerk::end() const
{
    return IndexClerkIter(d_unusedStack.begin());
}

inline
int IndexClerk::numDecommissionedIndices() const
{
    return static_cast<int>(d_unusedStack.size());
}

inline
int IndexClerk::nextNewIndex() const
{
    return d_nextNewIndex;
}

inline
int IndexClerk::maxSupportedBdexVersion(int /* versionSelector */)
{
    return 1;
}

#ifndef BDE_OMIT_INTERNAL_DEPRECATED  // pending deprecation

// DEPRECATED METHODS
inline
int IndexClerk::maxSupportedBdexVersion()
{
    return maxSupportedBdexVersion(0);
}

#endif // BDE_OMIT_INTERNAL_DEPRECATED -- pending deprecation

}  // close package namespace

// FREE OPERATORS
inline
bool bdlc::operator==(const IndexClerk& lhs, const IndexClerk& rhs)
{
    return lhs.d_nextNewIndex == rhs.d_nextNewIndex
        && lhs.d_unusedStack  == rhs.d_unusedStack;
}

inline
bool bdlc::operator!=(const IndexClerk& lhs, const IndexClerk& rhs)
{
    return lhs.d_nextNewIndex != rhs.d_nextNewIndex
        || lhs.d_unusedStack  != rhs.d_unusedStack;
}

inline
bsl::ostream& bdlc::operator<<(bsl::ostream& stream, const IndexClerk& rhs)
{
    return rhs.print(stream, 0, -1);
}

}  // close enterprise namespace

#endif

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