//***************************************************************************
//
//  (c) 1998-1999 by Microsoft Corp.
//
//  FLEXARRY.CPP
//
//  CFlexArray implementation (non-arena).
//
//  15-Jul-97  raymcc   Created.
//   8-Jun-98  bobw     cleaned up for WBEMPERF usage
//
//***************************************************************************

#include "wpheader.h"
#include <stdio.h>

//***************************************************************************
//
//  CFlexArray::CFlexArray
//
//  Constructs the array.
//
//  Parameters:
//  <nSize>         The starting preallocated size of the array.
//  <nGrowBy>       The amount to grow by when the array fills up.
//
//  Size() returns the number of elements in use, not the 'true' size.
//
//***************************************************************************
// ok
CFlexArray::CFlexArray(
    int nSize,
    int nGrowBy
    )
{
    m_nExtent = nSize;
    m_nSize = 0;
    m_nGrowBy = nGrowBy;
    m_hHeap = GetProcessHeap(); // call this once and save heap handle locally

    m_pArray = (void **) ALLOCMEM(m_hHeap, HEAP_ZERO_MEMORY, sizeof(void *) * nSize);

	assert (m_pArray != NULL);
}

//***************************************************************************
//
//  CFlexArray::~CFlexArray
//
//***************************************************************************
// ok
CFlexArray::~CFlexArray()
{
    FREEMEM(m_hHeap, 0, m_pArray);
}

//***************************************************************************
//
//  Copy constructor.
//
//  Copies the pointers, not their contents.
//
//***************************************************************************
// ok
CFlexArray::CFlexArray(CFlexArray &Src)
{
    m_pArray = 0;
    m_nSize = 0;
    m_nExtent = 0;
    m_nGrowBy = 0;

    *this = Src;
}

//***************************************************************************
//
//  operator =
//
//  Assignment operator.
//
//  Arenas are not copied.  This allows transfer of arrays between arenas.
//  Arrays are copied by pointer only.
//
//***************************************************************************
// ok
CFlexArray& CFlexArray::operator=(CFlexArray &Src)
{
    m_nSize   = Src.m_nSize;
    m_nExtent = Src.m_nExtent;
    m_nGrowBy = Src.m_nGrowBy;

    FREEMEM (m_hHeap, 0, m_pArray);
    m_pArray = (void **) ALLOCMEM(m_hHeap, HEAP_ZERO_MEMORY, sizeof(void *) * m_nExtent);
    if (m_pArray) {
        memcpy(m_pArray, Src.m_pArray, sizeof(void *) * m_nExtent);
    }

    return *this;
}

//***************************************************************************
//
//  CFlexArray::RemoveAt
//
//  Removes the element at the specified location.  Does not
//  actually delete the pointer. Shrinks the array over the top of
//  the 'doomed' element.
//
//  Parameters:
//  <nIndex>    The location of the element.
//
//  Return value:
//  range_error     The index is not legal.
//  no_error        Success.
//
//***************************************************************************
// ok
int CFlexArray::RemoveAt(int nIndex)
{
    int i;

    if (nIndex >= m_nSize) {
        return range_error;
    }

    for (i = nIndex; i < m_nSize - 1; i++) {
        m_pArray[i] = m_pArray[i + 1];
    }

    m_nSize--;
    m_pArray[m_nSize] = 0;

    return no_error;
}

//***************************************************************************
//
//  CFlexArray::InsertAt
//
//  Inserts a new element at the specified location.  The pointer is copied.
//
//  Parameters:
//  <nIndex>        The 0-origin location at which to insert the new element.
//  <pSrc>          The pointer to copy. (contents are not copied).
//
//  Return value:
//  array_full
//  out_of_memory
//  no_error
//
//***************************************************************************
// ok
int CFlexArray::InsertAt(int nIndex, void *pSrc)
{
    void    **pTmp; // pointer to new array
    int     nReturn = no_error;
    LONG    lOldSize;
    LONG    lNewSize;

    // If the array is full, we need to expand it.
    // ===========================================

    if (m_nSize == m_nExtent) {
        if (m_nGrowBy == 0) {
            nReturn  = array_full;
        } else {
            // compute sizes
            lOldSize = sizeof(void *) * m_nExtent;
            m_nExtent += m_nGrowBy;
            lNewSize = sizeof(void *) * m_nExtent;

            // allocate new array
            pTmp = (void **) ALLOCMEM(m_hHeap, HEAP_ZERO_MEMORY, lNewSize);
            if (!pTmp) {
                nReturn = out_of_memory;
            } else {
                // move bits from old array to new array
                memcpy (pTmp, m_pArray, lOldSize);
                // toss old arrya
                FREEMEM (m_hHeap, 0, m_pArray);
                // save pointer to new array
                m_pArray = pTmp;
            }
        }
    }

    // Special case of appending.  This is so frequent
    // compared to true insertion that we want to optimize.
    // ====================================================

    if (nReturn == no_error) {
        if (nIndex == m_nSize)  {
            m_pArray[m_nSize++] = pSrc;
        } else {
            // If here, we are inserting at some random location.
            // We start at the end of the array and copy all the elements
            // one position farther to the end to make a 'hole' for
            // the new element.
            // ==========================================================

            for (int i = m_nSize; i > nIndex; i--) {
                m_pArray[i] = m_pArray[i - 1];
            }

            m_pArray[nIndex] = pSrc;
            m_nSize++;
        }
    }
    return nReturn;
}

//***************************************************************************
//
//  CFlexArray::DebugDump
//
//***************************************************************************
//
void CFlexArray::DebugDump()
{
    printf("----CFlexArray Debug Dump----\n");
    printf("m_pArray = 0x%p\n", m_pArray);
    printf("m_nSize = %d\n", m_nSize);
    printf("m_nExtent = %d\n", m_nExtent);
    printf("m_nGrowBy = %d\n", m_nGrowBy);

    for (int i = 0; i < m_nExtent; i++)
    {
        if (i < m_nSize) {
            printf("![%d] = %p\n", i, m_pArray[i]);
        } else {
            printf("?[%d] = %p\n", i, m_pArray[i]);
        }
    }
}

//***************************************************************************
//
//  CFlexArray::Compress
//
//  Removes NULL elements by moving all non-NULL pointers to the beginning
//  of the array.  The array "Size" changes, but the extent is untouched.
//
//***************************************************************************
// ok
void CFlexArray::Compress()
{
    int nLeftCursor = 0, nRightCursor = 0;

    while (nLeftCursor < m_nSize - 1) {
        if (m_pArray[nLeftCursor]) {
            nLeftCursor++;
            continue;
        }
        else {
            nRightCursor = nLeftCursor + 1;
            while (m_pArray[nRightCursor] == 0 && nRightCursor < m_nSize)
                nRightCursor++;
            if (nRightCursor == m_nSize)
                break;  // Short circuit, no more nonzero elements.
            m_pArray[nLeftCursor] = m_pArray[nRightCursor];
            m_pArray[nRightCursor] = 0;
        }
    }

    while (m_pArray[m_nSize - 1] == 0 && m_nSize > 0) m_nSize--;
}

//***************************************************************************
//
//  CFlexArray::Empty
//
//  Clears the array of all pointers (does not deallocate them) and sets
//  its apparent size to zero.
//
//***************************************************************************
// ok
void CFlexArray::Empty()
{
    FREEMEM(m_hHeap, 0, m_pArray);
    m_pArray = (void **) ALLOCMEM(m_hHeap, HEAP_ZERO_MEMORY, sizeof(void *) * m_nGrowBy);
    m_nSize = 0;
    m_nExtent = m_nGrowBy;
}

//***************************************************************************
//
//  CFlexArray::UnbindPtr
//
//  Empties the array and returns the pointer to the data it contained
//
//***************************************************************************

void** CFlexArray::UnbindPtr()
{
    void** pp = m_pArray;
    Empty();
    return pp;
}