// Dynamic Array APIs
//*******************************************************************************************
//
// Filename : Da.c
//	
//				Implementation of DPAs
//
// Copyright (c) 1994 - 1996 Microsoft Corporation. All rights reserved
//
//*******************************************************************************************


#include "pch.h"
#include "dpda.h"
#include <assert.h>

#ifdef DEBUG
#define DPA_MAGIC   ('P' | ('A' << 256))
#define IsDPA(pdpa) ((pdpa) && (pdpa)->magic == DPA_MAGIC)
#else
#define IsDPA(pdsa)
#endif


#define ControlAlloc(hheap, cb)       HeapAlloc((hheap), HEAP_ZERO_MEMORY, (cb))
#define ControlReAlloc(hheap, pb, cb) HeapReAlloc((hheap), HEAP_ZERO_MEMORY, (pb),(cb))
#define ControlFree(hheap, pb)        HeapFree((hheap), 0, (pb))
#define ControlSize(hheap, pb)        HeapSize((hheap), 0, (LPCVOID)(pb))

typedef struct {
    void **pp;
    PFNDPACOMPARE pfnCmp;
    LPARAM lParam;
    int cp;
    void **ppT;
} SORTPARAMS;

BOOL DPA_MergeSort(SORTPARAMS * psp);
void DPA_MergeSort2(SORTPARAMS * psp, int iFirst, int cItems);



//================== Dynamic pointer array implementation ===========

typedef struct _DPA {
// NOTE: The following two fields MUST be defined in this order, at
// the beginning of the structure in order for the macro APIs to work.
//
    int cp;
    void **pp;

    HANDLE hheap;        // Heap to allocate from if NULL use shared

    int cpAlloc;
    int cpGrow;
#ifdef DEBUG
  UINT magic;
#endif
} DPA;

HANDLE g_hSharedHeap = NULL;

void * Alloc(long cb)
{
    // I will assume that this is the only one that needs the checks to
    // see if the heap has been previously created or not

    if (g_hSharedHeap == NULL)
    {
        g_hSharedHeap = HeapCreate(0, 1, 0);

        // If still NULL we have problems!
        if (g_hSharedHeap == NULL)
            return(NULL);
    }

    return HeapAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, cb);
}

void * ReAlloc(void * pb, long cb)
{
    if (pb == NULL)
        return Alloc(cb);
    return HeapReAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, pb, cb);
}

BOOL Free(void * pb)
{
    return HeapFree(g_hSharedHeap, 0, pb);
}

DWORD GetSize(void * pb)
{
    return HeapSize(g_hSharedHeap, 0, pb);
}


HDPA DPA_Create(int cpGrow)
{
    HDPA pdpa = Alloc(sizeof(DPA));
    if (pdpa)
    {
        pdpa->cp = 0;
        pdpa->cpAlloc = 0;
        pdpa->cpGrow = (cpGrow < 8 ? 8 : cpGrow);
        pdpa->pp = NULL;
        pdpa->hheap = g_hSharedHeap;   // Defaults to use shared one (for now...)
#ifdef DEBUG
        pdpa->magic = DPA_MAGIC;
#endif
    }
    return pdpa;
}

// Should nuke the standard DPA above...
HDPA DPA_CreateEx(int cpGrow, HANDLE hheap)
{
    HDPA pdpa;
    if (hheap == NULL)
    {
        pdpa = Alloc(sizeof(DPA));
        hheap = g_hSharedHeap;
    }
    else
        pdpa = ControlAlloc(hheap, sizeof(DPA));
    if (pdpa)
    {
        pdpa->cp = 0;
        pdpa->cpAlloc = 0;
        pdpa->cpGrow = (cpGrow < 8 ? 8 : cpGrow);
        pdpa->pp = NULL;
        pdpa->hheap = hheap;
#ifdef DEBUG
        pdpa->magic = DPA_MAGIC;
#endif
    }
    return pdpa;
}

BOOL DPA_Destroy(HDPA pdpa)
{
    //assert(IsDPA(pdpa));

    if (pdpa == NULL)       // allow NULL for low memory cases, still assert
        return TRUE;

    assert (pdpa->hheap);

#ifdef DEBUG
    pdpa->cp = 0;
    pdpa->cpAlloc = 0;
    pdpa->magic = 0;
#endif
    if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp))
        return FALSE;

    return ControlFree(pdpa->hheap, pdpa);
}

HDPA DPA_Clone(HDPA pdpa, HDPA pdpaNew)
{
    BOOL fAlloc = FALSE;

    if (!pdpaNew)
    {
        pdpaNew = DPA_CreateEx(pdpa->cpGrow, pdpa->hheap);
        if (!pdpaNew)
            return NULL;

        fAlloc = TRUE;
    }

    if (!DPA_Grow(pdpaNew, pdpa->cpAlloc)) {
	if (!fAlloc)
	    DPA_Destroy(pdpaNew);
	return NULL;
    }

    pdpaNew->cp = pdpa->cp;
    hmemcpy(pdpaNew->pp, pdpa->pp, pdpa->cp * sizeof(void *));

    return pdpaNew;
}

void * DPA_GetPtr(HDPA pdpa, int index)
{
//    assert(IsDPA(pdpa));

    if (index < 0 || index >= pdpa->cp)
        return NULL;

    return pdpa->pp[index];
}

int DPA_GetPtrIndex(HDPA pdpa, void * p)
{
    void **pp;
    void **ppMax;

    if (pdpa->pp)
    {
        pp = pdpa->pp;
        ppMax = pp + pdpa->cp;
        for ( ; pp < ppMax; pp++)
        {
            if (*pp == p)
                return (pp - pdpa->pp);
        }
    }
    return -1;
}

BOOL DPA_Grow(HDPA pdpa, int cpAlloc)
{
    if (cpAlloc > pdpa->cpAlloc)
    {
        void **ppNew;

        cpAlloc = ((cpAlloc + pdpa->cpGrow - 1) / pdpa->cpGrow) * pdpa->cpGrow;

        if (pdpa->pp)
            ppNew = (void * *)ControlReAlloc(pdpa->hheap, pdpa->pp, cpAlloc * sizeof(void *));
        else
            ppNew = (void * *)ControlAlloc(pdpa->hheap, cpAlloc * sizeof(void *));
        if (!ppNew)
            return FALSE;

        pdpa->pp = ppNew;
        pdpa->cpAlloc = cpAlloc;
    }
    return TRUE;
}

BOOL DPA_SetPtr(HDPA pdpa, int index, void * p)
{
    if (index < 0)
    {
        // DebugMsg(DM_ERROR, "DPA: Invalid index: %d", index);
        return FALSE;
    }

    if (index >= pdpa->cp)
    {
        if (!DPA_Grow(pdpa, index + 1))
            return FALSE;
        pdpa->cp = index + 1;
    }

    pdpa->pp[index] = p;

    return TRUE;
}

int DPA_InsertPtr(HDPA pdpa, int index, void * p)
{
    if (index < 0)
    {
        return -1;
    }
    if (index > pdpa->cp)
        index = pdpa->cp;

    // Make sure we have room for one more item
    //
    if (pdpa->cp + 1 > pdpa->cpAlloc)
    {
        if (!DPA_Grow(pdpa, pdpa->cp + 1))
            return -1;
    }

    // If we are inserting, we need to slide everybody up
    //
    if (index < pdpa->cp)
    {
        hmemcpy(&pdpa->pp[index + 1], &pdpa->pp[index],
            (pdpa->cp - index) * sizeof(void *));
    }

    pdpa->pp[index] = p;
    pdpa->cp++;

    return index;
}

void * DPA_DeletePtr(HDPA pdpa, int index)
{
    void * p;

//    assert(IsDPA(pdpa));

    if (index < 0 || index >= pdpa->cp)
    {
     //   DebugMsg(DM_ERROR, "DPA: Invalid index: %d", index);
        return NULL;
    }

    p = pdpa->pp[index];

    if (index < pdpa->cp - 1)
    {
        hmemcpy(&pdpa->pp[index], &pdpa->pp[index + 1],
            (pdpa->cp - (index + 1)) * sizeof(void *));
    }
    pdpa->cp--;

    if (pdpa->cpAlloc - pdpa->cp > pdpa->cpGrow)
    {
        void **ppNew;
        ppNew = ControlReAlloc(pdpa->hheap, pdpa->pp, (pdpa->cpAlloc - pdpa->cpGrow) * sizeof(void *));

        assert(ppNew);
        pdpa->pp = ppNew;
        pdpa->cpAlloc -= pdpa->cpGrow;
    }
    return p;
}

BOOL DPA_DeleteAllPtrs(HDPA pdpa)
{
    if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp))
        return FALSE;
    pdpa->pp = NULL;
    pdpa->cp = pdpa->cpAlloc = 0;
    return TRUE;
}

BOOL DPA_Sort(HDPA pdpa, PFNDPACOMPARE pfnCmp, LPARAM lParam)
{
    SORTPARAMS sp;

    sp.cp = pdpa->cp;
    sp.pp = pdpa->pp;
    sp.pfnCmp = pfnCmp;
    sp.lParam = lParam;

    return DPA_MergeSort(&sp);
}

#define SortCompare(psp, pp1, i1, pp2, i2) \
	(psp->pfnCmp(pp1[i1], pp2[i2], psp->lParam))

//
//  This function merges two sorted lists and makes one sorted list.
//   psp->pp[iFirst, iFirst+cItes/2-1], psp->pp[iFirst+cItems/2, iFirst+cItems-1]
//
void DPA_MergeThem(SORTPARAMS * psp, int iFirst, int cItems)
{
    //
    // Notes:
    //  This function is separated from DPA_MergeSort2() to avoid comsuming
    // stack variables. Never inline this.
    //
    int cHalf = cItems/2;
    int iIn1, iIn2, iOut;
    LPVOID * ppvSrc = &psp->pp[iFirst];

    // Copy the first part to temp storage so we can write directly into
    // the final buffer.  Note that this takes at most psp->cp/2 DWORD's
    hmemcpy(psp->ppT, ppvSrc, cHalf*sizeof(LPVOID));

    for (iIn1=0, iIn2=cHalf, iOut=0;;)
    {
	if (SortCompare(psp, psp->ppT, iIn1, ppvSrc, iIn2) <= 0) {
	    ppvSrc[iOut++] = psp->ppT[iIn1++];

	    if (iIn1==cHalf) {
		// We used up the first half; the rest of the second half
		// should already be in place
		break;
	    }
	} else {
	    ppvSrc[iOut++] = ppvSrc[iIn2++];
	    if (iIn2==cItems) {
		// We used up the second half; copy the rest of the first half
		// into place
		hmemcpy(&ppvSrc[iOut], &psp->ppT[iIn1], (cItems-iOut)*sizeof(LPVOID));
		break;
	    }
	}
    }
}

//
//  This function sorts a give list (psp->pp[iFirst,iFirst-cItems-1]).
//
void DPA_MergeSort2(SORTPARAMS * psp, int iFirst, int cItems)
{
    //
    // Notes:
    //   This function is recursively called. Therefore, we should minimize
    //  the number of local variables and parameters. At this point, we
    //  use one local variable and three parameters.
    //
    int cHalf;

    switch(cItems)
    {
    case 1:
	return;

    case 2:
	// Swap them, if they are out of order.
	if (SortCompare(psp, psp->pp, iFirst, psp->pp, iFirst+1) > 0)
	{
	    psp->ppT[0] = psp->pp[iFirst];
	    psp->pp[iFirst] = psp->pp[iFirst+1];
	    psp->pp[iFirst+1] = psp->ppT[0];
	}
	break;

    default:
    	cHalf = cItems/2;
	// Sort each half
	DPA_MergeSort2(psp, iFirst, cHalf);
	DPA_MergeSort2(psp, iFirst+cHalf, cItems-cHalf);
	// Then, merge them.
	DPA_MergeThem(psp, iFirst, cItems);
	break;
    }
}

BOOL DPA_MergeSort(SORTPARAMS * psp)
{
    if (psp->cp==0)
	return TRUE;

    // Note that we divide by 2 below; we want to round down
    psp->ppT = LocalAlloc(LPTR, psp->cp/2 * sizeof(LPVOID));
    if (!psp->ppT)
	return FALSE;

    DPA_MergeSort2(psp, 0, psp->cp);
    LocalFree(psp->ppT);
    return TRUE;
}

// Search function
//
int DPA_Search(HDPA pdpa, void * pFind, int iStart,
            PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT options)
{
    int cp = DPA_GetPtrCount(pdpa);

    assert(pfnCompare);
    assert(0 <= iStart);

    // Only allow these wierd flags if the list is sorted
    assert((options & DPAS_SORTED) || !(options & (DPAS_INSERTBEFORE | DPAS_INSERTAFTER)));

    if (!(options & DPAS_SORTED))
    {
        // Not sorted: do lisearch.
        int i;

        for (i = iStart; i < cp; i++)
        {
            if (0 == pfnCompare(pFind, DPA_FastGetPtr(pdpa, i), lParam))
                return i;
        }
        return -1;
    }
    else
    {
        // Search the array using binary search.  If several adjacent 
        // elements match the target element, the index of the first
        // matching element is returned.

        int iRet = -1;      // assume no match
        BOOL bFound = FALSE;
        int nCmp = 0;
        int iLow = 0;       // Don't bother using iStart for binary search
        int iMid = 0;
        int iHigh = cp - 1;

        // (OK for cp == 0)
        while (iLow <= iHigh)
        {
            iMid = (iLow + iHigh) / 2;

            nCmp = pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid), lParam);

            if (0 > nCmp)
                iHigh = iMid - 1;       // First is smaller
            else if (0 < nCmp)
                iLow = iMid + 1;        // First is larger
            else
            {
                // Match; search back for first match
                bFound = TRUE;
                while (0 < iMid)
                {
                    if (0 != pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid-1), lParam))
                        break;
                    else
                        iMid--;
                }
                break;
            }
        }

        if (bFound)
        {
            assert(0 <= iMid);
            iRet = iMid;
        }

        // Did the search fail AND
        // is one of the strange search flags set?
        if (!bFound && (options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE)))
        {
            // Yes; return the index where the target should be inserted
            // if not found
            if (0 < nCmp)       // First is larger
                iRet = iLow;
            else
                iRet = iMid;
            // (We don't distinguish between the two flags anymore)
        }
        else if ( !(options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE)) )
        {
            // Sanity check with lisearch
            assert(DPA_Search(pdpa, pFind, iStart, pfnCompare, lParam, options & ~DPAS_SORTED) == iRet);
        }
        return iRet;
    }
}

//===========================================================================
//
// String ptr management routines
//
// Copy as much of *psz to *pszBuf as will fit
//
int Str_GetPtr(LPCSTR psz, LPSTR pszBuf, int cchBuf)
{
    int cch = 0;

    // if pszBuf is NULL, just return length of string.
    //
    if (!pszBuf && psz)
        return lstrlen(psz);

    if (cchBuf)
    {
        if (psz)
        {
            cch = lstrlen(psz);

            if (cch > cchBuf - 1)
                cch = cchBuf - 1;

            hmemcpy(pszBuf, psz, cch);
        }
        pszBuf[cch] = 0;
    }
    return cch;
}

BOOL Str_Set(LPSTR *ppsz, LPCSTR psz)
{
    if (!psz)
    {
        if (*ppsz)
        {
            LocalFree(*ppsz);
            *ppsz = NULL;
        }
    }
    else
    {
	LPSTR pszNew;
        UINT cbSize = lstrlen(psz) + 1;
	if (*ppsz)
	    pszNew = LocalReAlloc(*ppsz, cbSize, LMEM_MOVEABLE | LMEM_ZEROINIT);
	else
	    pszNew = LocalAlloc(LPTR, cbSize);

        if (!pszNew)
            return FALSE;

        lstrcpy(pszNew, psz);
        *ppsz = pszNew;
    }
    return TRUE;
}

// Set *ppsz to a copy of psz, reallocing as needed
//
BOOL Str_SetPtr(LPSTR * ppsz, LPCSTR psz)
{
    if (!psz)
    {
        if (*ppsz)
        {
            Free(*ppsz);
            *ppsz = NULL;
        }
    }
    else
    {
        LPSTR pszNew = (LPSTR)ReAlloc(*ppsz, lstrlen(psz) + 1);
        if (!pszNew)
            return FALSE;

        lstrcpy(pszNew, psz);
        *ppsz = pszNew;
    }
    return TRUE;
}

