611 lines
13 KiB
C
611 lines
13 KiB
C
// 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;
|
|
}
|
|
|