//+---------------------------------------------------------------------------
//
//  Microsoft Forms
//  Copyright (C) Microsoft Corporation, 1992 - 1996.
//
//  File:       formsary.hxx
//
//  Contents:   CImplAry* classes
//
//              Stolen from Trident
//
//----------------------------------------------------------------------------

//+------------------------------------------------------------------------
//
// This is the implementation of the generic resizeable array classes. There
// are four array classes:
//
// CPtrAry<ELEM> --
//
//       Dynamic array class which is optimized for sizeof(ELEM) equal
//       to 4. The array is initially empty with no space or memory allocated
//       for data.
//
// CDataAry<ELEM> --
//
//       Same as CPtrAry but where sizeof(ELEM) is != 4 and less than 128.
//
// CStackPtrAry<ELEM, N> --
//
//       Dynamic array class optimized for sizeof(ELEM) equal to 4.
//       Space for N elements is allocated as member data of the class. If
//       this class is created on the stack, then space for N elements will
//       be created on the stack. The class can grow beyond N elements, at
//       which point memory will be allocated for the array data.
//
// CStackDataAry<ELEM, N> --
//
//       Same as CStackPtrAry, but where sizeof(ELEM) is != 4 and less than 128.
//
//
// All four classes have virtually the same methods, and are used the same.
// The only difference is that the DataAry classes have AppendIndirect and
// InsertIndirect, while the PtrAry classes use Append and Insert. The reason
// for the difference is that the Indirect methods take a pointer to the data,
// while the non-indirect methods take the actual data as an argument.
//
// The Stack arrays (CStackPtrAry and CStackDataAry) are used to pre-allocate
// space for elements in the array. This is useful if you create the array on
// the stack and you know that most of the time the array will be less than
// a certain number of elements. Creating one of these arrays on the stack
// allocates the array on the stack as well, preventing a separate memory
// allocation. Only if the array grows beyond the initial size will any
// additional memory be allocated.
//
// The fastest and most efficient way of looping through all elements in
// the array is as follows:
//
//            ELEM * pElem;
//            int    i;
//
//            for (i = aryElems.Size(), pElem = aryElems;
//                 i > 0;
//                 i--, pElem++)
//            {
//                (*pElem)->DoSomething();
//            }
//
// This loop syntax has been shown to be the fastest and produce the smallest
// code. Here's an example using a real data type:
//
//            CStackPtrAry<CSite*, 16> arySites;
//            CSite **ppSite;
//            int     i;
//
//            // Populate the array.
//            ...
//
//            // Now loop through every element in the array.
//            for (i = arySites.Size(), ppSite = arySites;
//                 i > 0;
//                 i--, ppSite++)
//            {
//                (*ppSite)->DoSomething();
//            }
//
// METHOD DESCRIPTIONS:
//
// Commonly used methods:
//
//        Size()             Returns the number of elements currently stored
//                           in the array.
//
//        operator []        Returns the given element in the array.
//
//        Item(int i)        Returns the given element in the array.
//
//        operator ELEM*     Allows the array class to be cast to a pointer
//                           to ELEM. Returns a pointer to the first element
//                           in the array. (Same as a Base() method).
//
//        Append(ELEM e)     Adds a new pointer to the end of the array,
//                           growing the array if necessary.  Only valid
//                           for arrays of pointers (CPtrAry, CStackPtrAry).
//
//        AppendIndirect(ELEM *pe, ELEM** ppePlaced)
//                           As Append, for non-pointer arrays
//                           (CDataAry, CStackDataAry).
//                           pe [in] - Pointer to element to add to array. The
//                                     data is copied into the array. Can be
//                                     NULL, in which case the new element is
//                                     initialized to all zeroes.
//                           ppePlaced [out] - Returns pointer to the new
//                                     element. Can be NULL.
//
//        Insert(int i, ELEM e)
//                           Inserts a new element (e) at the given index (i)
//                           in the array, growing the array if necessary. Any
//                           elements at or following the index are moved
//                           out of the way.
//
//        InsertIndirect(int i, ELEM *pe)
//                           As Insert, for non-pointer arrays
//                                 (CDataAry, CStackDataAry).
//
//        Find(ELEM e)       Returns the index at which a given element (e)
//                           is found (CPtrAry, CStackPtrAry).
//
//        FindIndirect(ELEM *pe)
//                           As Find, for non-pointer arrays
//                                 (CDataAry, CStackDataAry).
//
//        DeleteAll()        Empties the array and de-allocates associated
//                           memory.
//
//        Delete(int i)      Deletes an element of the array, moving any
//                           elements that follow it to fill
//
//        DeleteMultiple(int start, int end)
//                           Deletes a range of elements from the array,
//                           moving to fill. [start] and [end] are the indices
//                           of the start and end elements (inclusive).
//
//        DeleteByValue(ELEM e)
//                           Delete the element matching the given value.
//
//        DeleteByValueIndirect(ELEM *pe)
//                           As DeleteByValue, for non-pointer arrays.
//                                    (CDataAry, CStackDataAry).
//
//
// Less commonly used methods:
//
//        EnsureSize(long c) If you know how many elements you are going to put
//                          in the array before you actually do it, you can use
//                          EnsureSize to allocate the memory all at once instead
//                          of relying on Append(Indirect) to grow the array. This
//                          can be much more efficient (by causing only a single
//                          memory allocation instead of many) than just using
//                          Append(Indirect). You pass in the number of elements
//                          that memory should be allocated for. Note that this
//                          does not affect the "Size" of the array, which is
//                          the number of elements currently stored in it.
//
//        SetSize(int c)    Sets the "Size" of the array, which is the number
//                          of elements currently stored in it. SetSize will not
//                          allocate memory if you're growing the array.
//                          EnsureSize must be called first to reserve space if
//                          the array is growing. Setting the size smaller does
//                          not de-allocate memory, it just chops off the
//                          elements at the end of the array.
//
//        Grow(int c)       Equivalent to calling EnsureSize(c) followed by
//                          SetSize(c).
//
//        ReleaseAll()      (CPtrAry and CStackPtrAry only) Calls Release()
//                          on each element in the array and empties the array.
//
//        ReleaseAndDelete(int idx)
//                          (CPtrAry and CStackPtrAry only) Calls Release() on
//                          the given element and removes it from the array.
//
//           (See the class definitions below for signatures of the following
//            methods and src\core\cdutil\formsary.cxx for argument
//            descriptions)
//
//        CopyAppend        Appends data from another array (of the same type)
//                          to the end.
//
//        Copy              Copies data from another array (of the same type)
//                          into this array, replacing any existing data.
//
//        CopyAppendIndirect  Appends data from a C-style array of element data
//                          to the end of this array.
//
//        CopyIndirect      Copies elements from a C-style array into this array
//                          replacing any existing data.
//
//        EnumElements      Create an enumerator which supports the given
//                          interface ID for the contents of the array
//
//        EnumVARIANT       Create an IEnumVARIANT enumerator.
//
//        operator void *   Allow the CImplAry class to be cast
//                          to a (void *). Avoid using if possible - use
//                          the type-safe operator ELEM * instead.
//
//        ClearAndReset     Obsolete. Do not use.
//
//
//----------------------------------------------------------------------------





//----------------------------------------------------------------------------
//
// Class:     CImplAry
//
// Purpose:   Base implementation of all the dynamic array classes.
//
// Interface:
//
//        Deref       Returns a pointer to an element of the array;
//                    should only be used by derived classes. Use the
//                    type-safe methods operator[] or Item() instead.
//
//        GetAlloced  Get number of elements allocated
//
//  Members:    _c          Current size of the array
//              _pv         Buffer storing the elements
//
//  Note:       The CImplAry class only supports arrays of elements
//              whose size is less than 128.
//
//-------------------------------------------------------------------------


class CImplAry
{
    friend class CImplPtrAry;

private:
                DECLARE_MEMALLOC_NEW_DELETE();
public:
                ~CImplAry();
    inline int         Size() const    { return _c; } // UNIX: long->int for min() macro
    inline void        SetSize(int c)  { _c = c; }
    inline operator void *()           { return PData(); }
    void        DeleteAll();

    // BUGBUG -- This method should be protected, but I don't want to convert
    // existing code that uses it. (lylec)
    void *      Deref(size_t cb, int i);

#if DBG == 1
    BOOL _fCheckLock ; // If set with TraceTag CImplAryLock then any change
                       // (addition or deletion to the DataAry will generate an assert.

    void        LockCheck(BOOL fState)
      { _fCheckLock = fState; }
#else
    void        LockCheck(BOOL)
      {  }
#endif

    NO_COPY(CImplAry);

protected:

    //  Methods which are wrapped by inline subclass methods

                CImplAry();

    HRESULT     EnsureSize(size_t cb, long c);
    HRESULT     Grow(size_t cb, int c);
    HRESULT     AppendIndirect(size_t cb, void * pv, void ** ppvPlaced=NULL);
    HRESULT     InsertIndirect(size_t cb, int i, void * pv);
    int         FindIndirect(size_t cb, void *);

    void        Delete(size_t cb, int i);
    BOOL        DeleteByValueIndirect(size_t cb, void *pv);
    void        DeleteMultiple(size_t cb, int start, int end);

    HRESULT     CopyAppend(size_t cb, const CImplAry& ary, BOOL fAddRef);
    HRESULT     Copy(size_t cb, const CImplAry& ary, BOOL fAddRef);
    HRESULT     CopyIndirect(size_t cb, int c, void * pv, BOOL fAddRef);

    ULONG       GetAlloced(size_t cb);

    HRESULT     EnumElements(
                        size_t  cb,
                        REFIID  iid,
                        void ** ppv,
                        BOOL    fAddRef,
                        BOOL    fCopy = TRUE,
                        BOOL    fDelete = TRUE);

    HRESULT     EnumVARIANT(
                        size_t  cb,
                        VARTYPE         vt,
                        IEnumVARIANT ** ppenum,
                        BOOL            fCopy = TRUE,
                        BOOL            fDelete = TRUE);

    inline BOOL        UsingStackArray()
                    { return _fDontFree; }

    UINT        GetStackSize()
                    { Assert(_fStack);
                      return *(UINT*)((BYTE*)this + sizeof(CImplAry)); }
    void *      GetStackPtr()
                    { Assert(_fStack);
                      return (void*)((BYTE*)this + sizeof(CImplAry) + sizeof(int)); }

    unsigned long   _fStack     :1  ;  // Set if we're a stack-based array.
    unsigned long   _fDontFree  :1  ;  // Cleared if _pv points to alloced memory.
    unsigned long   _c          :30 ; // Count of elements

    void *      _pv;

    inline void * & PData()    { return _pv; }
};

//+------------------------------------------------------------------------
//
//  Member:     CImplAry::CImplAry
//
//+------------------------------------------------------------------------
inline
CImplAry::CImplAry()
{
    memset(this, 0, sizeof(CImplAry));
}

//+------------------------------------------------------------------------
//
//  Member:     CImplAry::Deref
//
//  Synopsis:   Returns a pointer to the i'th element of the array. This
//              method is normally called by type-safe methods in derived
//              classes.
//
//  Arguments:  i
//
//  Returns:    void *
//
//-------------------------------------------------------------------------

inline void *
CImplAry::Deref(size_t cb, int i)
{
    Assert(i >= 0);
    Assert(ULONG( i ) < GetAlloced(cb));
    return ((BYTE *) PData()) + i * cb;
}

//+------------------------------------------------------------------------
//
//  Class:      CImplPtrAry (ary)
//
//  Purpose:    Subclass used for arrays of pointers.  In this case, the
//              element size is known to be sizeof(void *).  Normally, the
//              CPtrAry template is used to define a specific concrete
//              implementation of this class, to hold a specific type of
//              pointer.
//
//              See documentation above for use.
//
//-------------------------------------------------------------------------

class CImplPtrAry : public CImplAry
{
protected:
    DECLARE_MEMALLOC_NEW_DELETE();

    CImplPtrAry() : CImplAry() {};

    HRESULT     Append(void * pv);
    HRESULT     Insert(int i, void * pv);
    int         Find(void * pv);
    BOOL        DeleteByValue(void *pv);

    HRESULT     CopyAppend(const CImplAry& ary, BOOL fAddRef);
    HRESULT     Copy(const CImplAry& ary, BOOL fAddRef);
    HRESULT     CopyIndirect(int c, void * pv, BOOL fAddRef);


public:

    HRESULT     ClearAndReset();

    HRESULT     EnsureSize(long c);

    HRESULT     Grow(int c);

    void        Delete(int i);
    void        DeleteMultiple(int start, int end);

    void        ReleaseAll();
    void        ReleaseAndDelete(int idx);
};


//+---------------------------------------------------------------------------
//
//  Class:      CDataAry
//
//  Purpose:    This template class declares a concrete derived class
//              of CImplAry.
//
//              See documentation above for use.
//
//----------------------------------------------------------------------------

template <class ELEM>
class CDataAry : public CImplAry
{
public:
    DECLARE_MEMALLOC_NEW_DELETE();

    CDataAry() : CImplAry() { }
    operator ELEM *() { return (ELEM *)PData(); }
    CDataAry(const CDataAry &);

    ELEM & Item(int i) { return *(ELEM*)Deref(sizeof(ELEM), i); }

    HRESULT     EnsureSize(long c)
                    { return CImplAry::EnsureSize(sizeof(ELEM), c); }
    HRESULT     Grow(int c)
                    { return CImplAry::Grow(sizeof(ELEM), c); }
    HRESULT     AppendIndirect(ELEM * pe, ELEM ** ppePlaced=NULL)
                    { return CImplAry::AppendIndirect(sizeof(ELEM), (void*)pe, (void**)ppePlaced); }
    ELEM *      Append()
                    { ELEM * pElem; return AppendIndirect( NULL, & pElem ) ? NULL : pElem; }
    HRESULT     InsertIndirect(int i, ELEM * pe)
                    { return CImplAry::InsertIndirect(sizeof(ELEM), i, (void*)pe); }
    int         FindIndirect(ELEM * pe)
                    { return CImplAry::FindIndirect(sizeof(ELEM), (void*)pe); }

    void        Delete(int i)
                    { CImplAry::Delete(sizeof(ELEM), i); }
    BOOL        DeleteByValueIndirect(ELEM *pe)
                    { return CImplAry::DeleteByValueIndirect(sizeof(ELEM), (void*)pe); }
    void        DeleteMultiple(int start, int end)
                    { CImplAry::DeleteMultiple(sizeof(ELEM), start, end); }

    HRESULT     CopyAppend(const CDataAry<ELEM>& ary, BOOL fAddRef)
                    { return CImplAry::Copy(sizeof(ELEM), ary, fAddRef); }
    HRESULT     Copy(const CDataAry<ELEM>& ary, BOOL fAddRef)
                    { return CImplAry::Copy(sizeof(ELEM), ary, fAddRef); }
    HRESULT     CopyIndirect(int c, ELEM * pv, BOOL fAddRef)
                    { return CImplAry::CopyIndirect(sizeof(ELEM), c, (void*)pv, fAddRef); }
};

//+---------------------------------------------------------------------------
//
//  Class:      CPtrAry
//
//  Purpose:    This template class declares a concrete derived class
//              of CImplPtrAry.
//
//              See documentation above for use.
//
//----------------------------------------------------------------------------

template <class ELEM>
class CPtrAry : public CImplPtrAry
{
public:
    DECLARE_MEMALLOC_NEW_DELETE();

    CPtrAry() : CImplPtrAry() { Assert(sizeof(ELEM) == sizeof(void*)); }
    operator ELEM *() { return (ELEM *)PData(); }
    CPtrAry(const CPtrAry &);

    ELEM & Item(int i) { return *(ELEM*)Deref(sizeof(ELEM), i); }

    HRESULT     Append(ELEM e)
                    { return CImplPtrAry::Append((void*)e); }
    HRESULT     Insert(int i, ELEM e)
                    { return CImplPtrAry::Insert(i, (void*)e); }
    BOOL        DeleteByValue(ELEM e)
                    { return CImplPtrAry::DeleteByValue((void*)e); }
    int         Find(ELEM e)
                    { return CImplPtrAry::Find((void*)e); }

    HRESULT     CopyAppend(const CPtrAry<ELEM>& ary, BOOL fAddRef)
                    { return CImplPtrAry::Copy(ary, fAddRef); }
    HRESULT     Copy(const CPtrAry<ELEM>& ary, BOOL fAddRef)
                    { return CImplPtrAry::Copy(ary, fAddRef); }
    HRESULT     CopyIndirect(int c, ELEM *pe, BOOL fAddRef)
                    { return CImplPtrAry::CopyIndirect(c, (void*)pe, fAddRef); }

};

//+---------------------------------------------------------------------------
//
//  Class:      CStackDataAry
//
//  Purpose:    Declares a CDataAry that has initial storage on the stack.
//              N elements are declared on the stack, and the array will
//              grow dynamically beyond that if necessary.
//
//              See documentation above for use.
//
//----------------------------------------------------------------------------

template <class ELEM, int N>
class CStackDataAry : public CDataAry<ELEM>
{
public:
    DECLARE_MEMALLOC_NEW_DELETE();

    CStackDataAry() : CDataAry<ELEM>()
    {
        _cStack     = N;
        _fStack     = TRUE;
        _fDontFree  = TRUE;
        PData()     = (void *) & _achTInit;
    }

protected:
    int   _cStack;                     // Must be first data member.
    char  _achTInit[N*sizeof(ELEM)];
};

//+---------------------------------------------------------------------------
//
//  Class:      CStackPtrAry
//
//  Purpose:    Same as CStackDataAry except for pointer types.
//
//              See documentation above for use.
//
//----------------------------------------------------------------------------

template <class ELEM, int N>
class CStackPtrAry : public CPtrAry<ELEM>
{
public:
    DECLARE_MEMALLOC_NEW_DELETE();

    CStackPtrAry() : CPtrAry<ELEM>()
    {
        _cStack     = N;
        _fStack     = TRUE;
        _fDontFree  = TRUE;
        PData()     = (void *) & _achTInit;
    }

protected:
    int   _cStack;                     // Must be first data member.
    char  _achTInit[N*sizeof(ELEM)];
};