2090 lines
43 KiB
C
2090 lines
43 KiB
C
|
//
|
||
|
// Copyright (c) Microsoft Corporation 1993-1995
|
||
|
//
|
||
|
// mem.c
|
||
|
//
|
||
|
// This file contains memory management and dynamic
|
||
|
// array functions.
|
||
|
//
|
||
|
// History:
|
||
|
// 09-27-94 ScottH Taken from commctrl
|
||
|
// 04-29-95 ScottH Taken from briefcase and cleaned up
|
||
|
//
|
||
|
|
||
|
|
||
|
#include "proj.h"
|
||
|
#include "common.h"
|
||
|
|
||
|
#ifndef NOMEM
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#ifndef WIN32
|
||
|
//
|
||
|
// Subsegment Allocation for 16-bit
|
||
|
//
|
||
|
|
||
|
#define MAX_WORD 0xffff
|
||
|
|
||
|
DECLARE_HANDLE(HHEAP);
|
||
|
|
||
|
typedef struct
|
||
|
{ // maps to the bottom of a 16bit DS
|
||
|
WORD reserved[8];
|
||
|
WORD cAlloc;
|
||
|
WORD cbAllocFailed;
|
||
|
HHEAP hhpFirst;
|
||
|
HHEAP hhpNext;
|
||
|
} HEAP;
|
||
|
|
||
|
#define PHEAP(hhp) ((HEAP FAR*)MAKELP(hhp, 0))
|
||
|
#define MAKEHP(sel, off) ((void _huge*)MAKELP((sel), (off)))
|
||
|
|
||
|
#define CBSUBALLOCMAX 0x0000f000L
|
||
|
|
||
|
HHEAP g_hhpFirst = NULL;
|
||
|
|
||
|
BOOL NEAR DestroyHeap(HHEAP hhp);
|
||
|
|
||
|
void Mem_Terminate()
|
||
|
{
|
||
|
while (g_hhpFirst)
|
||
|
DestroyHeap(g_hhpFirst);
|
||
|
}
|
||
|
|
||
|
BOOL NEAR CreateHeap(WORD cbInitial)
|
||
|
{
|
||
|
HHEAP hhp;
|
||
|
|
||
|
if (cbInitial < 1024)
|
||
|
cbInitial = 1024;
|
||
|
|
||
|
hhp = (HHEAP)GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_SHARE, cbInitial);
|
||
|
|
||
|
if (!hhp)
|
||
|
return FALSE;
|
||
|
|
||
|
if (!LocalInit((WORD)hhp, sizeof(HEAP), cbInitial - 1))
|
||
|
{
|
||
|
GlobalFree(hhp);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
PHEAP(hhp)->cAlloc = 0;
|
||
|
PHEAP(hhp)->cbAllocFailed = MAX_WORD;
|
||
|
PHEAP(hhp)->hhpNext = g_hhpFirst;
|
||
|
g_hhpFirst = hhp;
|
||
|
|
||
|
DebugMsg(DM_TRACE, "CreateHeap: added new local heap %x", hhp);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
#pragma optimize("o", off) // linked list removals don't optimize correctly
|
||
|
BOOL NEAR DestroyHeap(HHEAP hhp)
|
||
|
{
|
||
|
ASSERT(hhp);
|
||
|
ASSERT(g_hhpFirst);
|
||
|
|
||
|
if (g_hhpFirst == hhp)
|
||
|
{
|
||
|
g_hhpFirst = PHEAP(hhp)->hhpNext;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
HHEAP hhpT = g_hhpFirst;
|
||
|
|
||
|
while (PHEAP(hhpT)->hhpNext != hhp)
|
||
|
{
|
||
|
hhpT = PHEAP(hhpT)->hhpNext;
|
||
|
if (!hhpT)
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
PHEAP(hhpT)->hhpNext = PHEAP(hhp)->hhpNext;
|
||
|
}
|
||
|
if (GlobalFree((HGLOBAL)hhp) != NULL)
|
||
|
return FALSE;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
#pragma optimize("", on) // back to default optimizations
|
||
|
|
||
|
#pragma optimize("lge", off) // Suppress warnings associated with use of _asm...
|
||
|
void NEAR* NEAR HeapAlloc(HHEAP hhp, WORD cb)
|
||
|
{
|
||
|
void NEAR* pb;
|
||
|
|
||
|
_asm {
|
||
|
push ds
|
||
|
mov ds,hhp
|
||
|
}
|
||
|
|
||
|
pb = (void NEAR*)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, cb);
|
||
|
|
||
|
if (pb)
|
||
|
((HEAP NEAR*)0)->cAlloc++;
|
||
|
|
||
|
_asm {
|
||
|
pop ds
|
||
|
}
|
||
|
|
||
|
return pb;
|
||
|
}
|
||
|
#pragma optimize("o", off) // linked list removals don't optimize correctly
|
||
|
|
||
|
void _huge* WINAPI SharedAlloc(long cb)
|
||
|
{
|
||
|
void NEAR* pb;
|
||
|
HHEAP hhp;
|
||
|
HHEAP hhpPrev;
|
||
|
|
||
|
// If this is a big allocation, just do a global alloc.
|
||
|
//
|
||
|
if (cb > CBSUBALLOCMAX)
|
||
|
{
|
||
|
void FAR* lpb = MAKEHP(GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_SHARE, cb), 0);
|
||
|
if (!lpb)
|
||
|
DebugMsg(DM_ERROR, "Alloc: out of memory");
|
||
|
return lpb;
|
||
|
}
|
||
|
|
||
|
hhp = g_hhpFirst;
|
||
|
|
||
|
while (TRUE)
|
||
|
{
|
||
|
if (hhp == NULL)
|
||
|
{
|
||
|
if (!CreateHeap(0))
|
||
|
{
|
||
|
DebugMsg(DM_ERROR, "Alloc: out of memory");
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
hhp = g_hhpFirst;
|
||
|
}
|
||
|
|
||
|
pb = HeapAlloc(hhp, (WORD)cb);
|
||
|
if (pb)
|
||
|
return MAKEHP(hhp, pb);
|
||
|
|
||
|
// Record the size of the allocation that failed.
|
||
|
// Later attempts to allocate more than this amount
|
||
|
// will not succeed. This gets reset anytime anything
|
||
|
// is freed in the heap.
|
||
|
//
|
||
|
PHEAP(hhp)->cbAllocFailed = (WORD)cb;
|
||
|
|
||
|
// First heap is full... see if there's room in any other heap...
|
||
|
//
|
||
|
for (hhpPrev = hhp; hhp = PHEAP(hhp)->hhpNext; hhpPrev = hhp)
|
||
|
{
|
||
|
// If the last allocation to fail in this heap
|
||
|
// is not larger than cb, don't even try an allocation.
|
||
|
//
|
||
|
if ((WORD)cb >= PHEAP(hhp)->cbAllocFailed)
|
||
|
continue;
|
||
|
|
||
|
pb = HeapAlloc(hhp, (WORD)cb);
|
||
|
if (pb)
|
||
|
{
|
||
|
// This heap had room: move it to the front...
|
||
|
//
|
||
|
PHEAP(hhpPrev)->hhpNext = PHEAP(hhp)->hhpNext;
|
||
|
PHEAP(hhp)->hhpNext = g_hhpFirst;
|
||
|
g_hhpFirst = hhp;
|
||
|
|
||
|
return MAKEHP(hhp, pb);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// The alloc failed. Set cbAllocFailed...
|
||
|
//
|
||
|
PHEAP(hhp)->cbAllocFailed = (WORD)cb;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
#pragma optimize("", on) // back to default optimizations
|
||
|
|
||
|
#pragma optimize("lge", off) // Suppress warnings associated with use of _asm...
|
||
|
|
||
|
void _huge* WINAPI SharedReAlloc(void _huge* pb, long cb)
|
||
|
{
|
||
|
void NEAR* pbNew;
|
||
|
void _huge* lpbNew;
|
||
|
UINT cbOld;
|
||
|
|
||
|
// does not work with cb > 64k
|
||
|
if (!pb)
|
||
|
return SharedAlloc(cb);
|
||
|
|
||
|
if (OFFSETOF(pb) == 0)
|
||
|
return MAKEHP(GlobalReAlloc((HGLOBAL)SELECTOROF(pb), cb, GMEM_MOVEABLE | GMEM_ZEROINIT), 0);
|
||
|
|
||
|
_asm {
|
||
|
push ds
|
||
|
mov ds,word ptr [pb+2]
|
||
|
}
|
||
|
|
||
|
pbNew = (void NEAR*)LocalReAlloc((HLOCAL)OFFSETOF(pb), (int)cb, LMEM_MOVEABLE | LMEM_ZEROINIT);
|
||
|
if (!pbNew)
|
||
|
cbOld = LocalSize((HLOCAL)OFFSETOF(pb));
|
||
|
|
||
|
_asm {
|
||
|
pop ds
|
||
|
}
|
||
|
|
||
|
if (pbNew)
|
||
|
return MAKEHP(SELECTOROF(pb), pbNew);
|
||
|
|
||
|
lpbNew = SharedAlloc(cb);
|
||
|
if (lpbNew)
|
||
|
{
|
||
|
hmemcpy((void FAR*)lpbNew, (void FAR*)pb, cbOld);
|
||
|
Free(pb);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DebugMsg(DM_ERROR, "ReAlloc: out of memory");
|
||
|
}
|
||
|
return lpbNew;
|
||
|
}
|
||
|
|
||
|
BOOL WINAPI SharedFree(void _huge* FAR * ppb)
|
||
|
{
|
||
|
BOOL fSuccess;
|
||
|
UINT cAlloc;
|
||
|
void _huge * pb = *ppb;
|
||
|
|
||
|
if (!pb)
|
||
|
return FALSE;
|
||
|
|
||
|
*ppb = 0;
|
||
|
|
||
|
if (OFFSETOF(pb) == 0)
|
||
|
return (GlobalFree((HGLOBAL)SELECTOROF(pb)) == NULL);
|
||
|
|
||
|
_asm {
|
||
|
push ds
|
||
|
mov ds,word ptr [pb+2]
|
||
|
}
|
||
|
|
||
|
fSuccess = (LocalFree((HLOCAL)OFFSETOF(pb)) ? FALSE : TRUE);
|
||
|
|
||
|
cAlloc = 1;
|
||
|
if (fSuccess)
|
||
|
{
|
||
|
cAlloc = --((HEAP NEAR*)0)->cAlloc;
|
||
|
((HEAP NEAR*)0)->cbAllocFailed = MAX_WORD;
|
||
|
}
|
||
|
|
||
|
_asm {
|
||
|
pop ds
|
||
|
}
|
||
|
|
||
|
if (cAlloc == 0)
|
||
|
DestroyHeap((HHEAP)SELECTOROF(pb));
|
||
|
|
||
|
return fSuccess;
|
||
|
}
|
||
|
|
||
|
|
||
|
DWORD WINAPI SharedGetSize(void _huge* pb)
|
||
|
{
|
||
|
WORD wSize;
|
||
|
|
||
|
if (OFFSETOF(pb) == 0)
|
||
|
return GlobalSize((HGLOBAL)SELECTOROF(pb));
|
||
|
|
||
|
_asm {
|
||
|
push ds
|
||
|
mov ds,word ptr [pb+2]
|
||
|
}
|
||
|
|
||
|
wSize = LocalSize((HLOCAL)OFFSETOF(pb));
|
||
|
|
||
|
_asm {
|
||
|
pop ds
|
||
|
}
|
||
|
|
||
|
return (DWORD)wSize;
|
||
|
}
|
||
|
|
||
|
#pragma optimize("", on)
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
|
||
|
#else // WIN32
|
||
|
//
|
||
|
// Win32 memory management wrappers
|
||
|
//
|
||
|
|
||
|
// Define a Global Shared Heap that we use to allocate memory
|
||
|
// out of that we need to share between multiple instances.
|
||
|
//
|
||
|
static HANDLE g_hSharedHeap = NULL;
|
||
|
|
||
|
#define MAXHEAPSIZE 2097152
|
||
|
#define HEAP_SHARED 0x04000000 /* put heap in shared memory */
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Clean up heap. This function should be called at
|
||
|
the program's termination.
|
||
|
|
||
|
Returns: --
|
||
|
Cond: --
|
||
|
*/
|
||
|
void PUBLIC Mem_Terminate()
|
||
|
{
|
||
|
// Assuming that everything else has exited
|
||
|
//
|
||
|
if (g_hSharedHeap != NULL)
|
||
|
HeapDestroy(g_hSharedHeap);
|
||
|
g_hSharedHeap = NULL;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Copies psz into *ppszBuf. Will alloc or realloc *ppszBuf
|
||
|
accordingly.
|
||
|
|
||
|
If psz is NULL, this function frees *ppszBuf. This is
|
||
|
the preferred method of freeing the allocated buffer.
|
||
|
|
||
|
Returns: TRUE on success
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC GSetString(
|
||
|
LPSTR * ppszBuf,
|
||
|
LPCSTR psz) // NULL to free *ppszBuf
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
|
||
|
ASSERT(ppszBuf);
|
||
|
|
||
|
// Free the buffer?
|
||
|
if (!psz)
|
||
|
{
|
||
|
// Yes
|
||
|
if (ppszBuf)
|
||
|
{
|
||
|
GFree(*ppszBuf);
|
||
|
*ppszBuf = NULL;
|
||
|
}
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No; (re)allocate and set buffer
|
||
|
DWORD cb = CbFromCch(lstrlen(psz)+CCH_NUL);
|
||
|
|
||
|
if (*ppszBuf)
|
||
|
{
|
||
|
// Need to reallocate?
|
||
|
if (cb > GGetSize(*ppszBuf))
|
||
|
{
|
||
|
// Yes
|
||
|
LPSTR pszT = GReAlloc(*ppszBuf, cb);
|
||
|
if (pszT)
|
||
|
{
|
||
|
*ppszBuf = pszT;
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ppszBuf = (LPSTR)GAlloc(cb);
|
||
|
if (*ppszBuf)
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bRet)
|
||
|
{
|
||
|
ASSERT(*ppszBuf);
|
||
|
lstrcpy(*ppszBuf, psz);
|
||
|
}
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Concatenates psz onto *ppszBuf. Will alloc or
|
||
|
realloc *ppszBuf accordingly.
|
||
|
|
||
|
Returns: TRUE on success
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC GCatString(
|
||
|
LPSTR * ppszBuf,
|
||
|
LPCSTR psz)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
DWORD cb;
|
||
|
|
||
|
ASSERT(ppszBuf);
|
||
|
ASSERT(psz);
|
||
|
|
||
|
cb = CbFromCch(lstrlen(psz)+CCH_NUL);
|
||
|
|
||
|
if (*ppszBuf)
|
||
|
{
|
||
|
// (Don't need to count nul because it is already counted in cb)
|
||
|
DWORD cbExisting = CbFromCch(lstrlen(*ppszBuf));
|
||
|
|
||
|
// Need to reallocate?
|
||
|
if ((cb+cbExisting) > GGetSize(*ppszBuf))
|
||
|
{
|
||
|
// Yes; realloc at least MAX_BUF to cut down on the amount
|
||
|
// of calls in the future
|
||
|
LPSTR pszT = GReAlloc(*ppszBuf, cbExisting+max(cb, MAX_BUF));
|
||
|
if (pszT)
|
||
|
{
|
||
|
*ppszBuf = pszT;
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ppszBuf = (LPSTR)GAlloc(max(cb, MAX_BUF));
|
||
|
if (*ppszBuf)
|
||
|
{
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bRet)
|
||
|
{
|
||
|
ASSERT(*ppszBuf);
|
||
|
lstrcat(*ppszBuf, psz);
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
//
|
||
|
// Shared heap memory management
|
||
|
//
|
||
|
#ifndef NOSHAREDHEAP
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Allocate out of shared heap
|
||
|
|
||
|
Returns: Pointer to allocate memory
|
||
|
Cond: --
|
||
|
*/
|
||
|
void * PUBLIC SharedAlloc(
|
||
|
DWORD 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)
|
||
|
{
|
||
|
ENTER_EXCLUSIVE()
|
||
|
{
|
||
|
if (g_hSharedHeap == NULL)
|
||
|
{
|
||
|
g_hSharedHeap = HeapCreate(HEAP_SHARED, 1, MAXHEAPSIZE);
|
||
|
}
|
||
|
}
|
||
|
LEAVE_EXCLUSIVE()
|
||
|
|
||
|
// If still NULL we have problems!
|
||
|
if (g_hSharedHeap == NULL)
|
||
|
return(NULL);
|
||
|
}
|
||
|
|
||
|
return HeapAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, cb);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Realloc out of shared heap.
|
||
|
|
||
|
Returns: Possibly new pointer to resized block
|
||
|
Cond: --
|
||
|
*/
|
||
|
void * PUBLIC SharedReAlloc(
|
||
|
PVOID pv,
|
||
|
DWORD cb)
|
||
|
{
|
||
|
if (NULL == pv)
|
||
|
{
|
||
|
return SharedAlloc(cb);
|
||
|
}
|
||
|
return HeapReAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, pv, cb);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Free shared memory
|
||
|
|
||
|
Returns: --
|
||
|
Cond: --
|
||
|
*/
|
||
|
void PUBLIC _SharedFree(
|
||
|
PVOID pv)
|
||
|
{
|
||
|
ASSERT(pv);
|
||
|
|
||
|
if (pv)
|
||
|
{
|
||
|
HeapFree(g_hSharedHeap, 0, pv);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Returns the allocated size of a block
|
||
|
|
||
|
Returns: see above
|
||
|
Cond: --
|
||
|
*/
|
||
|
DWORD PUBLIC SharedGetSize(
|
||
|
PVOID pv)
|
||
|
{
|
||
|
return HeapSize(g_hSharedHeap, 0, pv);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Copies psz into *ppszBuf. Will alloc or realloc *ppszBuf
|
||
|
accordingly.
|
||
|
|
||
|
If psz is NULL, this function frees *ppszBuf. This is
|
||
|
the preferred method of freeing the allocated buffer.
|
||
|
|
||
|
Returns: TRUE on success
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC SharedSetString(
|
||
|
LPSTR * ppszBuf,
|
||
|
LPCSTR psz) // NULL to free *ppszBuf
|
||
|
{
|
||
|
BOOL bRet;
|
||
|
|
||
|
ASSERT(ppszBuf);
|
||
|
|
||
|
// Free the buffer?
|
||
|
if (!psz)
|
||
|
{
|
||
|
// Yes
|
||
|
if (ppszBuf)
|
||
|
{
|
||
|
SharedFree(*ppszBuf);
|
||
|
*ppszBuf = NULL;
|
||
|
}
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// No; (re)allocate and set buffer
|
||
|
DWORD cb = CbFromCch(lstrlen(psz)+CCH_NUL);
|
||
|
|
||
|
LPSTR pszT = SharedReAlloc(*ppszBuf, cb);
|
||
|
if (pszT)
|
||
|
{
|
||
|
*ppszBuf = pszT;
|
||
|
lstrcpy(*ppszBuf, psz);
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
else
|
||
|
bRet = FALSE;
|
||
|
}
|
||
|
return bRet;
|
||
|
}
|
||
|
#endif // NOSHAREDHEAP
|
||
|
|
||
|
|
||
|
//
|
||
|
// Memory tracking functions
|
||
|
//
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
typedef struct _HEAPTRACE
|
||
|
{
|
||
|
DWORD cAlloc;
|
||
|
DWORD cFailure;
|
||
|
DWORD cReAlloc;
|
||
|
DWORD cbMaxTotal;
|
||
|
DWORD cCurAlloc;
|
||
|
DWORD cbCurTotal;
|
||
|
} HEAPTRACE;
|
||
|
|
||
|
HEAPTRACE g_htSync = {0}; // Start of zero...
|
||
|
|
||
|
#endif // DEBUG
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Allocate from a heap.
|
||
|
|
||
|
Returns: pointer to block of memory
|
||
|
NULL (if out of memory)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
LPVOID PUBLIC MemAlloc(
|
||
|
HANDLE hheap,
|
||
|
DWORD cb)
|
||
|
{
|
||
|
LPVOID lp;
|
||
|
|
||
|
if (hheap)
|
||
|
{
|
||
|
lp = HeapAlloc(hheap, HEAP_ZERO_MEMORY, cb);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lp = GAlloc(cb);
|
||
|
}
|
||
|
|
||
|
if (lp == NULL)
|
||
|
{
|
||
|
DEBUG_CODE( g_htSync.cFailure++; )
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
// Update counts.
|
||
|
g_htSync.cAlloc++;
|
||
|
g_htSync.cCurAlloc++;
|
||
|
g_htSync.cbCurTotal += cb;
|
||
|
if (g_htSync.cbCurTotal > g_htSync.cbMaxTotal)
|
||
|
g_htSync.cbMaxTotal = g_htSync.cbCurTotal;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
return lp;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Reallocate a block of memory in a given heap.
|
||
|
|
||
|
Returns: Pointer to reallocated block
|
||
|
NULL (if out of memory)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
LPVOID PUBLIC MemReAlloc(
|
||
|
HANDLE hheap,
|
||
|
LPVOID pb,
|
||
|
DWORD cb)
|
||
|
{
|
||
|
LPVOID lp;
|
||
|
DEBUG_CODE( DWORD cbOld; )
|
||
|
|
||
|
if (hheap)
|
||
|
{
|
||
|
DEBUG_CODE( cbOld = HeapSize(hheap, 0, pb); )
|
||
|
|
||
|
lp = HeapReAlloc(hheap, HEAP_ZERO_MEMORY, pb, cb);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (pb)
|
||
|
{
|
||
|
DEBUG_CODE( cbOld = GGetSize(pb); )
|
||
|
|
||
|
lp = GReAlloc(pb, cb);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DEBUG_CODE( cbOld = 0; )
|
||
|
|
||
|
lp = GAlloc(cb);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (lp == NULL)
|
||
|
{
|
||
|
DEBUG_CODE( g_htSync.cFailure++; )
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
// Update counts.
|
||
|
g_htSync.cReAlloc++;
|
||
|
g_htSync.cbCurTotal += cb - cbOld;
|
||
|
if (g_htSync.cbCurTotal > g_htSync.cbMaxTotal)
|
||
|
g_htSync.cbMaxTotal = g_htSync.cbCurTotal;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
return lp;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Free block of memory in heap.
|
||
|
|
||
|
Returns: TRUE
|
||
|
FALSE (if failure)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC MemFree(
|
||
|
HANDLE hheap,
|
||
|
LPVOID pb)
|
||
|
{
|
||
|
BOOL fRet;
|
||
|
DEBUG_CODE( DWORD cbOld; )
|
||
|
|
||
|
if (hheap)
|
||
|
{
|
||
|
DEBUG_CODE( cbOld = HeapSize(hheap, 0, pb); )
|
||
|
|
||
|
fRet = HeapFree(hheap, 0, pb);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DEBUG_CODE( cbOld = GGetSize(pb); )
|
||
|
|
||
|
GFree(pb);
|
||
|
fRet = TRUE;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
if (fRet)
|
||
|
{
|
||
|
// Update counts.
|
||
|
g_htSync.cCurAlloc--;
|
||
|
g_htSync.cbCurTotal -= cbOld;
|
||
|
}
|
||
|
|
||
|
#endif
|
||
|
|
||
|
return fRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Returns the size of the given block.
|
||
|
|
||
|
Returns: size in bytes
|
||
|
Cond: --
|
||
|
*/
|
||
|
DWORD PUBLIC MemSize(
|
||
|
HANDLE hheap,
|
||
|
LPVOID pb)
|
||
|
{
|
||
|
if (hheap)
|
||
|
return (DWORD)HeapSize(hheap, 0, pb);
|
||
|
else
|
||
|
return (DWORD)GGetSize(pb);
|
||
|
}
|
||
|
|
||
|
#endif // WIN32
|
||
|
|
||
|
|
||
|
//////////////////////////////////////////////////////////////////
|
||
|
|
||
|
|
||
|
#ifndef NODA
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Private alloc for pointer array functions.
|
||
|
|
||
|
Returns: pointer to block of memory
|
||
|
NULL (if out of memory)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
LPVOID PRIVATE PrvAlloc(
|
||
|
DWORD dwFlags, // PAF_* flags
|
||
|
HANDLE hheap,
|
||
|
DWORD cb)
|
||
|
{
|
||
|
LPVOID lp;
|
||
|
|
||
|
ASSERT(PAF_SHARED == SAF_SHARED);
|
||
|
|
||
|
if (IsFlagSet(dwFlags, PAF_SHARED))
|
||
|
{
|
||
|
lp = SharedAlloc(cb);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
lp = MemAlloc(hheap, cb);
|
||
|
}
|
||
|
|
||
|
return lp;
|
||
|
}
|
||
|
|
||
|
|
||
|
// Heapsort is a bit slower, but it doesn't use any stack or memory...
|
||
|
// Mergesort takes a bit of memory (O(n)) and stack (O(log(n)), but very fast...
|
||
|
//
|
||
|
#ifdef WIN32
|
||
|
#define MERGESORT
|
||
|
#else
|
||
|
#define USEHEAPSORT
|
||
|
#endif
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
#define SA_MAGIC ('S' | ('A' << 256))
|
||
|
#define IsSA(psa) ((psa) && (psa)->magic == SA_MAGIC)
|
||
|
#define PA_MAGIC ('P' | ('A' << 256))
|
||
|
#define IsPA(ppa) ((ppa) && (ppa)->magic == PA_MAGIC)
|
||
|
#else
|
||
|
#define IsSA(psa)
|
||
|
#define IsPA(ppa)
|
||
|
#endif
|
||
|
|
||
|
|
||
|
typedef struct
|
||
|
{
|
||
|
PVOID * pp;
|
||
|
PFNPACOMPARE pfnCmp;
|
||
|
LPARAM lParam;
|
||
|
int cp;
|
||
|
#ifdef MERGESORT
|
||
|
PVOID * ppT;
|
||
|
#endif
|
||
|
} SORTPARAMS;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Structure Array
|
||
|
//
|
||
|
|
||
|
typedef struct _SA
|
||
|
{
|
||
|
// NOTE: The following field MUST be defined at the beginning of the
|
||
|
// structure in order for SAGetCount() to work.
|
||
|
DWORD cItem; // number of elements in sa
|
||
|
|
||
|
PVOID aItem; // memory for elements
|
||
|
DWORD cItemAlloc; // number items which fit in aItem
|
||
|
DWORD cbItem; // size of each item
|
||
|
DWORD cItemGrow; // number items to grow cItemAlloc by
|
||
|
DWORD dwFlags;
|
||
|
HANDLE hheap;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
UINT magic;
|
||
|
#endif
|
||
|
} SA;
|
||
|
|
||
|
#define SA_PITEM(psa, index) ((PVOID)(((char FAR*)(psa)->aItem) + ((index) * (psa)->cbItem)))
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Create a structure array.
|
||
|
|
||
|
Returns: TRUE
|
||
|
FALSE (if out of memory or invalid parameters)
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC SACreateEx(
|
||
|
PHSA phsa,
|
||
|
DWORD cbItem,
|
||
|
DWORD cItemGrow,
|
||
|
HANDLE hheap, // Must be non-NULL if SAF_HEAP set
|
||
|
DWORD dwFlags)
|
||
|
{
|
||
|
HSA psa;
|
||
|
|
||
|
ASSERT(phsa);
|
||
|
ASSERT(0 < cbItem);
|
||
|
|
||
|
psa = PrvAlloc(dwFlags, hheap, sizeof(SA));
|
||
|
|
||
|
if (IsFlagSet(dwFlags, PAF_SHARED))
|
||
|
hheap = g_hSharedHeap;
|
||
|
|
||
|
if (psa)
|
||
|
{
|
||
|
psa->cItem = 0;
|
||
|
psa->cItemAlloc = 0;
|
||
|
psa->cbItem = cbItem;
|
||
|
psa->cItemGrow = (0 == cItemGrow ? 1 : cItemGrow);
|
||
|
psa->aItem = NULL;
|
||
|
psa->dwFlags = dwFlags;
|
||
|
psa->hheap = hheap;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
psa->magic = SA_MAGIC;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
*phsa = psa;
|
||
|
|
||
|
return NULL != psa;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Destroys a structure array.
|
||
|
|
||
|
Returns:
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC SADestroyEx(
|
||
|
HSA psa,
|
||
|
PFNSAFREE pfnFree,
|
||
|
LPARAM lParam)
|
||
|
{
|
||
|
ASSERT(IsSA(psa));
|
||
|
|
||
|
if (psa == NULL) // allow NULL for low memory cases, still assert
|
||
|
return TRUE;
|
||
|
|
||
|
if (psa->aItem)
|
||
|
{
|
||
|
if (pfnFree)
|
||
|
{
|
||
|
DWORD i = SAGetCount(psa);
|
||
|
|
||
|
while (0 < i)
|
||
|
{
|
||
|
i--;
|
||
|
|
||
|
// Caller should not free the actual pointer being
|
||
|
// passed, only the contents!
|
||
|
pfnFree(SA_PITEM(psa, i), lParam);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!MemFree(psa->hheap, psa->aItem))
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
psa->cItem = 0;
|
||
|
psa->cItemAlloc = 0;
|
||
|
psa->cbItem = 0;
|
||
|
psa->magic = 0;
|
||
|
#endif
|
||
|
|
||
|
return MemFree(psa->hheap, psa);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Copy structure at index into buffer.
|
||
|
|
||
|
Returns: TRUE
|
||
|
FALSE
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC SAGetItem(
|
||
|
HSA psa,
|
||
|
DWORD index,
|
||
|
PVOID pitem)
|
||
|
{
|
||
|
ASSERT(IsSA(psa));
|
||
|
ASSERT(pitem);
|
||
|
|
||
|
if (SA_ERR == index || index >= psa->cItem)
|
||
|
{
|
||
|
TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", index);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
hmemcpy(pitem, SA_PITEM(psa, index), psa->cbItem);
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Get pointer to structure in array
|
||
|
|
||
|
Returns: TRUE (if the index is within range)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC SAGetItemPtr(
|
||
|
HSA psa,
|
||
|
DWORD index,
|
||
|
LPVOID * ppv)
|
||
|
{
|
||
|
BOOL bRet;
|
||
|
|
||
|
ASSERT(IsSA(psa));
|
||
|
ASSERT(ppv);
|
||
|
|
||
|
bRet = !(SA_ERR == index || index >= psa->cItem);
|
||
|
|
||
|
if (bRet)
|
||
|
{
|
||
|
*ppv = SA_PITEM(psa, index);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", index);
|
||
|
*ppv = NULL;
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Set item
|
||
|
|
||
|
Returns:
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC SASetItem(
|
||
|
HSA psa,
|
||
|
DWORD index,
|
||
|
PVOID pitem)
|
||
|
{
|
||
|
ASSERT(pitem);
|
||
|
ASSERT(IsSA(psa));
|
||
|
|
||
|
if (SA_ERR == index)
|
||
|
{
|
||
|
TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", index);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (index >= psa->cItem)
|
||
|
{
|
||
|
if (index + 1 > psa->cItemAlloc)
|
||
|
{
|
||
|
int cItemAlloc = (((index + 1) + psa->cItemGrow - 1) / psa->cItemGrow) * psa->cItemGrow;
|
||
|
|
||
|
PVOID aItemNew = MemReAlloc(psa->hheap, psa->aItem, cItemAlloc * psa->cbItem);
|
||
|
if (!aItemNew)
|
||
|
return FALSE;
|
||
|
|
||
|
psa->aItem = aItemNew;
|
||
|
psa->cItemAlloc = cItemAlloc;
|
||
|
}
|
||
|
psa->cItem = index + 1;
|
||
|
}
|
||
|
|
||
|
hmemcpy(SA_PITEM(psa, index), pitem, psa->cbItem);
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Inserts the given item. If *piIndex is greater than
|
||
|
the current size of the array, the item is appended
|
||
|
to the end. Otherwise, the item is inserted at
|
||
|
*piIndex.
|
||
|
|
||
|
If piIndex is NULL, the item is appended to the end.
|
||
|
|
||
|
Use SASetItem to place an item at a specified index,
|
||
|
regardless of the array size.
|
||
|
|
||
|
When this function completes successfully, it sets
|
||
|
*piIndex to the index that the item really gets
|
||
|
inserted at. Otherwise, it sets *piIndex to SA_ERR.
|
||
|
|
||
|
Returns: TRUE (on successful insertion)
|
||
|
FALSE
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC SAInsertItem(
|
||
|
HSA psa,
|
||
|
LPDWORD pindex, // May be NULL
|
||
|
PVOID pitem)
|
||
|
{
|
||
|
BOOL bRet = TRUE; // assume success
|
||
|
|
||
|
ASSERT(pitem);
|
||
|
ASSERT(IsSA(psa));
|
||
|
|
||
|
if (pindex && SA_ERR == *pindex)
|
||
|
{
|
||
|
TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", *pindex);
|
||
|
bRet = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DWORD index;
|
||
|
|
||
|
if (NULL == pindex || *pindex > psa->cItem)
|
||
|
index = psa->cItem;
|
||
|
else
|
||
|
index = *pindex;
|
||
|
|
||
|
if (psa->cItem + 1 > psa->cItemAlloc)
|
||
|
{
|
||
|
PVOID aItemNew = MemReAlloc(psa->hheap, psa->aItem,
|
||
|
(psa->cItemAlloc + psa->cItemGrow) * psa->cbItem);
|
||
|
if (!aItemNew)
|
||
|
bRet = FALSE;
|
||
|
else
|
||
|
{
|
||
|
psa->aItem = aItemNew;
|
||
|
psa->cItemAlloc += psa->cItemGrow;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (bRet)
|
||
|
{
|
||
|
// If we are inserting, we need to slide everybody up
|
||
|
if (index < psa->cItem)
|
||
|
{
|
||
|
hmemcpy(SA_PITEM(psa, index + 1), SA_PITEM(psa, index),
|
||
|
(psa->cItem - index) * psa->cbItem);
|
||
|
}
|
||
|
psa->cItem++;
|
||
|
hmemcpy(SA_PITEM(psa, index), pitem, psa->cbItem);
|
||
|
|
||
|
if (pindex)
|
||
|
*pindex = index;
|
||
|
}
|
||
|
else if (pindex)
|
||
|
{
|
||
|
*pindex = SA_ERR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose:
|
||
|
Returns:
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC SADeleteItem(
|
||
|
HSA psa,
|
||
|
DWORD index)
|
||
|
{
|
||
|
ASSERT(IsSA(psa));
|
||
|
|
||
|
if (SA_ERR == index || index >= psa->cItem)
|
||
|
{
|
||
|
TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", index);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (index < psa->cItem - 1)
|
||
|
{
|
||
|
hmemcpy(SA_PITEM(psa, index), SA_PITEM(psa, index + 1),
|
||
|
(psa->cItem - (index + 1)) * psa->cbItem);
|
||
|
}
|
||
|
psa->cItem--;
|
||
|
|
||
|
if (psa->cItemAlloc - psa->cItem > psa->cItemGrow)
|
||
|
{
|
||
|
PVOID aItemNew = MemReAlloc(psa->hheap, psa->aItem,
|
||
|
(psa->cItemAlloc - psa->cItemGrow) * psa->cbItem);
|
||
|
|
||
|
ASSERT(aItemNew);
|
||
|
psa->aItem = aItemNew;
|
||
|
psa->cItemAlloc -= psa->cItemGrow;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose:
|
||
|
Returns:
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC SADeleteAllItems(
|
||
|
HSA psa)
|
||
|
{
|
||
|
ASSERT(IsSA(psa));
|
||
|
|
||
|
if (psa->aItem)
|
||
|
{
|
||
|
MemFree(psa->hheap, psa->aItem);
|
||
|
}
|
||
|
|
||
|
psa->aItem = NULL;
|
||
|
psa->cItem = psa->cItemAlloc = 0;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
//================== Dynamic pointer array implementation ===========
|
||
|
|
||
|
typedef struct _PA {
|
||
|
// 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.
|
||
|
//
|
||
|
DWORD cp;
|
||
|
DWORD dwAlignPad;
|
||
|
PVOID * pp;
|
||
|
|
||
|
HANDLE hheap; // Heap to allocate from if NULL use shared
|
||
|
|
||
|
DWORD cpAlloc;
|
||
|
DWORD cpGrow;
|
||
|
DWORD dwFlags;
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
UINT magic;
|
||
|
#endif
|
||
|
} PA;
|
||
|
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Creates a pointer array.
|
||
|
|
||
|
Returns: TRUE
|
||
|
FALSE (if out of memory)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PACreateEx(
|
||
|
PHPA phpa,
|
||
|
DWORD cpGrow,
|
||
|
HANDLE hheap, // Must be non-null if PAF_HEAP set
|
||
|
DWORD dwFlags) // PAF_*
|
||
|
{
|
||
|
HPA ppa;
|
||
|
|
||
|
ASSERT(phpa);
|
||
|
|
||
|
ppa = PrvAlloc(dwFlags, hheap, sizeof(PA));
|
||
|
|
||
|
if (IsFlagSet(dwFlags, PAF_SHARED))
|
||
|
hheap = g_hSharedHeap;
|
||
|
|
||
|
if (ppa)
|
||
|
{
|
||
|
ppa->dwFlags = dwFlags;
|
||
|
ppa->cp = 0;
|
||
|
ppa->cpAlloc = 0;
|
||
|
ppa->cpGrow = (cpGrow < 8 ? 8 : cpGrow);
|
||
|
ppa->pp = NULL;
|
||
|
|
||
|
#ifdef WIN32
|
||
|
ppa->hheap = hheap;
|
||
|
#else
|
||
|
ppa->hheap = NULL;
|
||
|
#endif
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
ppa->magic = PA_MAGIC;
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
*phpa = ppa;
|
||
|
|
||
|
return NULL != ppa;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Destroy a pointer array, and call the given pfnFree
|
||
|
function for each element in the array.
|
||
|
|
||
|
Returns: TRUE
|
||
|
FALSE (on failure)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PADestroyEx(
|
||
|
HPA ppa,
|
||
|
PFNPAFREE pfnFree,
|
||
|
LPARAM lParam)
|
||
|
{
|
||
|
ASSERT(IsPA(ppa));
|
||
|
|
||
|
if (ppa == NULL) // allow NULL for low memory cases, still assert
|
||
|
return TRUE;
|
||
|
|
||
|
if (ppa->pp)
|
||
|
{
|
||
|
if (pfnFree)
|
||
|
{
|
||
|
DWORD i = PAGetCount(ppa);
|
||
|
|
||
|
while (0 < i)
|
||
|
{
|
||
|
i--;
|
||
|
pfnFree(PAFastGetPtr(ppa, i), lParam);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!MemFree(ppa->hheap, ppa->pp))
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
ppa->cp = 0;
|
||
|
ppa->cpAlloc = 0;
|
||
|
ppa->magic = 0;
|
||
|
#endif
|
||
|
|
||
|
return MemFree(ppa->hheap, ppa);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Clone a pointer array. If *phpa was previously
|
||
|
allocated, this function simply grows the array
|
||
|
to the appropriate size before copying the contents
|
||
|
of the array.
|
||
|
|
||
|
Returns: TRUE
|
||
|
FALSE (if out of memory)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PAClone(
|
||
|
PHPA phpa,
|
||
|
HPA ppa)
|
||
|
{
|
||
|
BOOL bRet;
|
||
|
HPA ppaNew;
|
||
|
|
||
|
ASSERT(phpa);
|
||
|
|
||
|
if (NULL == *phpa)
|
||
|
{
|
||
|
bRet = PACreateEx(&ppaNew, ppa->cpGrow, ppa->hheap, ppa->dwFlags);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ppaNew = *phpa;
|
||
|
bRet = TRUE;
|
||
|
}
|
||
|
|
||
|
if (bRet)
|
||
|
{
|
||
|
bRet = PAGrow(ppaNew, ppa->cpAlloc);
|
||
|
if (!bRet)
|
||
|
{
|
||
|
if (NULL == *phpa)
|
||
|
PADestroy(ppaNew);
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
ppaNew->cp = ppa->cp;
|
||
|
hmemcpy(ppaNew->pp, ppa->pp, ppa->cp * sizeof(PVOID));
|
||
|
*phpa = ppaNew;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Get a pointer stored in index
|
||
|
|
||
|
Returns: TRUE
|
||
|
FALSE (if index out of range)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PAGetPtr(
|
||
|
HPA ppa,
|
||
|
DWORD index,
|
||
|
LPVOID * ppv)
|
||
|
{
|
||
|
BOOL bRet;
|
||
|
|
||
|
ASSERT(IsPA(ppa));
|
||
|
ASSERT(ppv);
|
||
|
|
||
|
bRet = !(PA_ERR == index || index >= ppa->cp);
|
||
|
|
||
|
if (bRet)
|
||
|
{
|
||
|
*ppv = ppa->pp[index];
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
*ppv = NULL;
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Gets the index that pointer p is stored at
|
||
|
|
||
|
Returns: index
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PAGetPtrIndex(
|
||
|
HPA ppa,
|
||
|
PVOID p,
|
||
|
LPDWORD pindex)
|
||
|
{
|
||
|
BOOL bRet = FALSE;
|
||
|
PVOID * pp;
|
||
|
PVOID * ppMax;
|
||
|
|
||
|
ASSERT(IsPA(ppa));
|
||
|
ASSERT(pindex);
|
||
|
|
||
|
if (ppa->pp)
|
||
|
{
|
||
|
pp = ppa->pp;
|
||
|
ppMax = pp + ppa->cp;
|
||
|
for ( ; pp < ppMax; pp++)
|
||
|
{
|
||
|
if (*pp == p)
|
||
|
{
|
||
|
*pindex = (DWORD)(pp - ppa->pp);
|
||
|
bRet = TRUE;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!bRet)
|
||
|
*pindex = PA_ERR;
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Grow the pointer array
|
||
|
|
||
|
Returns:
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PAGrow(
|
||
|
HPA ppa,
|
||
|
DWORD cpAlloc)
|
||
|
{
|
||
|
ASSERT(IsPA(ppa));
|
||
|
|
||
|
if (cpAlloc > ppa->cpAlloc)
|
||
|
{
|
||
|
PVOID * ppNew;
|
||
|
|
||
|
cpAlloc = ((cpAlloc + ppa->cpGrow - 1) / ppa->cpGrow) * ppa->cpGrow;
|
||
|
|
||
|
if (ppa->pp)
|
||
|
ppNew = (PVOID *)MemReAlloc(ppa->hheap, ppa->pp, cpAlloc * sizeof(PVOID));
|
||
|
else
|
||
|
ppNew = (PVOID *)PrvAlloc(ppa->dwFlags, ppa->hheap, cpAlloc * sizeof(PVOID));
|
||
|
if (!ppNew)
|
||
|
return FALSE;
|
||
|
|
||
|
ppa->pp = ppNew;
|
||
|
ppa->cpAlloc = cpAlloc;
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Store a pointer at index. Grows the array accordingly.
|
||
|
|
||
|
Returns: TRUE
|
||
|
FALSE (if out of memory)
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PASetPtr(
|
||
|
HPA ppa,
|
||
|
DWORD index,
|
||
|
PVOID p)
|
||
|
{
|
||
|
ASSERT(IsPA(ppa));
|
||
|
|
||
|
if (PA_ERR == index)
|
||
|
{
|
||
|
TRACE_MSG(TF_ERROR, "PA: Invalid index: %lu", index);
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
if (index >= ppa->cp)
|
||
|
{
|
||
|
if (!PAGrow(ppa, index + 1))
|
||
|
return FALSE;
|
||
|
ppa->cp = index + 1;
|
||
|
}
|
||
|
|
||
|
ppa->pp[index] = p;
|
||
|
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Inserts the given item. If *piIndex is greater than
|
||
|
the current size of the array, the item is appended
|
||
|
to the end. Otherwise, the item is inserted at
|
||
|
*piIndex.
|
||
|
|
||
|
If piIndex is NULL, the item is appended to the end.
|
||
|
|
||
|
Use SASetItem to place an item at a specified index,
|
||
|
regardless of the array size.
|
||
|
|
||
|
When this function completes successfully, it sets
|
||
|
*piIndex to the index that the item really gets
|
||
|
inserted at. Otherwise, it sets *piIndex to SA_ERR.
|
||
|
|
||
|
Returns: TRUE (on successful insertion)
|
||
|
FALSE
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PAInsertPtr(
|
||
|
HPA ppa,
|
||
|
LPDWORD pindex, // May be NULL
|
||
|
PVOID p)
|
||
|
{
|
||
|
BOOL bRet;
|
||
|
|
||
|
ASSERT(IsPA(ppa));
|
||
|
|
||
|
if (pindex && PA_ERR == *pindex)
|
||
|
{
|
||
|
TRACE_MSG(TF_ERROR, "PA: Invalid index: %lu", *pindex);
|
||
|
bRet = FALSE;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
DWORD index;
|
||
|
|
||
|
bRet = TRUE; // assume success
|
||
|
|
||
|
if (NULL == pindex || *pindex > ppa->cp)
|
||
|
index = ppa->cp;
|
||
|
else
|
||
|
index = *pindex;
|
||
|
|
||
|
// Make sure we have room for one more item
|
||
|
//
|
||
|
if (ppa->cp + 1 > ppa->cpAlloc)
|
||
|
{
|
||
|
bRet = PAGrow(ppa, ppa->cp + 1);
|
||
|
}
|
||
|
|
||
|
if (bRet)
|
||
|
{
|
||
|
// If we are inserting, we need to slide everybody up
|
||
|
if (index < ppa->cp)
|
||
|
{
|
||
|
hmemcpy(&ppa->pp[index + 1], &ppa->pp[index],
|
||
|
(ppa->cp - index) * sizeof(PVOID));
|
||
|
}
|
||
|
|
||
|
ppa->pp[index] = p;
|
||
|
ppa->cp++;
|
||
|
|
||
|
if (pindex)
|
||
|
*pindex = index;
|
||
|
}
|
||
|
else if (pindex)
|
||
|
{
|
||
|
*pindex = PA_ERR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return bRet;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Delete a pointer from index.
|
||
|
|
||
|
Returns: the deleted pointer
|
||
|
NULL (if index is out of range)
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
PVOID PUBLIC PADeletePtr(
|
||
|
HPA ppa,
|
||
|
DWORD index)
|
||
|
{
|
||
|
PVOID p;
|
||
|
|
||
|
ASSERT(IsPA(ppa));
|
||
|
|
||
|
if (PA_ERR == index || index >= ppa->cp)
|
||
|
{
|
||
|
TRACE_MSG(TF_ERROR, "PA: Invalid index: %lu", index);
|
||
|
return NULL;
|
||
|
}
|
||
|
|
||
|
p = ppa->pp[index];
|
||
|
|
||
|
if (index < ppa->cp - 1)
|
||
|
{
|
||
|
hmemcpy(&ppa->pp[index], &ppa->pp[index + 1],
|
||
|
(ppa->cp - (index + 1)) * sizeof(PVOID));
|
||
|
}
|
||
|
ppa->cp--;
|
||
|
|
||
|
if (ppa->cpAlloc - ppa->cp > ppa->cpGrow)
|
||
|
{
|
||
|
PVOID * ppNew;
|
||
|
ppNew = MemReAlloc(ppa->hheap, ppa->pp, (ppa->cpAlloc - ppa->cpGrow) * sizeof(PVOID));
|
||
|
|
||
|
ASSERT(ppNew);
|
||
|
ppa->pp = ppNew;
|
||
|
ppa->cpAlloc -= ppa->cpGrow;
|
||
|
}
|
||
|
return p;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Delete all the pointers in the array. If pfnFree
|
||
|
is non-NULL, this function will free each of the
|
||
|
pointer elements in this array using pfnFree.
|
||
|
|
||
|
Returns: TRUE
|
||
|
FALSE
|
||
|
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PADeleteAllPtrsEx(
|
||
|
HPA ppa,
|
||
|
PFNPAFREE pfnFree,
|
||
|
LPARAM lParam)
|
||
|
{
|
||
|
ASSERT(IsPA(ppa));
|
||
|
|
||
|
if (ppa->pp)
|
||
|
{
|
||
|
if (pfnFree)
|
||
|
{
|
||
|
int i = PAGetCount(ppa);
|
||
|
|
||
|
while (0 < i)
|
||
|
{
|
||
|
i--;
|
||
|
pfnFree(PAFastGetPtr(ppa, i), lParam);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (!MemFree(ppa->hheap, ppa->pp))
|
||
|
return FALSE;
|
||
|
}
|
||
|
|
||
|
ppa->pp = NULL;
|
||
|
ppa->cp = ppa->cpAlloc = 0;
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
#ifdef USEQUICKSORT
|
||
|
BOOL NEAR PAQuickSort2(
|
||
|
DWORD i,
|
||
|
DWORD j,
|
||
|
SORTPARAMS FAR* psp)
|
||
|
{
|
||
|
PVOID * pp = psp->pp;
|
||
|
LPARAM lParam = psp->lParam;
|
||
|
PFNPACOMPARE pfnCmp = psp->pfnCmp;
|
||
|
|
||
|
DWORD iPivot;
|
||
|
PVOID pFirst;
|
||
|
DWORD k;
|
||
|
int result;
|
||
|
|
||
|
iPivot = PA_ERR;
|
||
|
pFirst = pp[i];
|
||
|
for (k = i + 1; k <= j; k++)
|
||
|
{
|
||
|
result = (*pfnCmp)(pp[k], pFirst, lParam);
|
||
|
|
||
|
if (result > 0)
|
||
|
{
|
||
|
iPivot = k;
|
||
|
break;
|
||
|
}
|
||
|
else if (result < 0)
|
||
|
{
|
||
|
iPivot = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (iPivot != PA_ERR)
|
||
|
{
|
||
|
DWORD l = i;
|
||
|
DWORD r = j;
|
||
|
PVOID pivot = pp[iPivot];
|
||
|
|
||
|
do
|
||
|
{
|
||
|
PVOID p;
|
||
|
|
||
|
p = pp[l];
|
||
|
pp[l] = pp[r];
|
||
|
pp[r] = p;
|
||
|
|
||
|
while ((*pfnCmp)(pp[l], pivot, lParam) < 0)
|
||
|
l++;
|
||
|
while ((*pfnCmp)(pp[r], pivot, lParam) >= 0)
|
||
|
r--;
|
||
|
}
|
||
|
while (l <= r);
|
||
|
|
||
|
if (l - 1 > i)
|
||
|
PAQuickSort2(i, l - 1, psp);
|
||
|
if (j > l)
|
||
|
PAQuickSort2(l, j, psp);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL NEAR PAQuickSort(
|
||
|
SORTPARAMS FAR* psp)
|
||
|
{
|
||
|
return PAQuickSort2(0, psp->cp - 1, psp);
|
||
|
}
|
||
|
#endif // USEQUICKSORT
|
||
|
|
||
|
#ifdef USEHEAPSORT
|
||
|
|
||
|
void NEAR PAHeapSortPushDown(
|
||
|
DWORD first,
|
||
|
DWORD last,
|
||
|
SORTPARAMS FAR* psp)
|
||
|
{
|
||
|
PVOID * pp = psp->pp;
|
||
|
LPARAM lParam = psp->lParam;
|
||
|
PFNPACOMPARE pfnCmp = psp->pfnCmp;
|
||
|
DWORD r;
|
||
|
DWORD r2;
|
||
|
|
||
|
r = first;
|
||
|
while (r <= last / 2)
|
||
|
{
|
||
|
int wRTo2R;
|
||
|
r2 = r * 2;
|
||
|
|
||
|
wRTo2R = (*pfnCmp)(pp[r-1], pp[r2-1], lParam);
|
||
|
|
||
|
if (r2 == last)
|
||
|
{
|
||
|
if (wRTo2R < 0)
|
||
|
{
|
||
|
Swap(pp[r-1], pp[r2-1]);
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
int wR2toR21 = (*pfnCmp)(pp[r2-1], pp[r2+1-1], lParam);
|
||
|
|
||
|
if (wRTo2R < 0 && wR2toR21 >= 0)
|
||
|
{
|
||
|
Swap(pp[r-1], pp[r2-1]);
|
||
|
r = r2;
|
||
|
}
|
||
|
else if ((*pfnCmp)(pp[r-1], pp[r2+1-1], lParam) < 0 && wR2toR21 < 0)
|
||
|
{
|
||
|
Swap(pp[r-1], pp[r2+1-1]);
|
||
|
r = r2 + 1;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL NEAR PAHeapSort(SORTPARAMS FAR* psp)
|
||
|
{
|
||
|
PVOID * pp = psp->pp;
|
||
|
DWORD c = psp->cp;
|
||
|
DWORD i;
|
||
|
|
||
|
for (i = c / 2; i >= 1; i--)
|
||
|
PAHeapSortPushDown(i, c, psp);
|
||
|
|
||
|
for (i = c; i >= 2; i--)
|
||
|
{
|
||
|
Swap(pp[0], pp[i-1]);
|
||
|
|
||
|
PAHeapSortPushDown(1, i - 1, psp);
|
||
|
}
|
||
|
return TRUE;
|
||
|
}
|
||
|
#endif // USEHEAPSORT
|
||
|
|
||
|
#if defined(MERGESORT) && defined(WIN32)
|
||
|
|
||
|
#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 NEAR PAMergeThem(
|
||
|
SORTPARAMS FAR* psp,
|
||
|
DWORD iFirst,
|
||
|
DWORD cItems)
|
||
|
{
|
||
|
//
|
||
|
// Notes:
|
||
|
// This function is separated from PAMergeSort2() to avoid comsuming
|
||
|
// stack variables. Never inline this.
|
||
|
//
|
||
|
DWORD cHalf = cItems/2;
|
||
|
DWORD 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 NEAR PAMergeSort2(
|
||
|
SORTPARAMS FAR* psp,
|
||
|
DWORD iFirst,
|
||
|
DWORD 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.
|
||
|
//
|
||
|
DWORD 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
|
||
|
PAMergeSort2(psp, iFirst, cHalf);
|
||
|
PAMergeSort2(psp, iFirst+cHalf, cItems-cHalf);
|
||
|
|
||
|
// Then, merge them.
|
||
|
PAMergeThem(psp, iFirst, cItems);
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
BOOL NEAR PAMergeSort(
|
||
|
SORTPARAMS FAR* psp)
|
||
|
{
|
||
|
if (psp->cp == 0)
|
||
|
return TRUE;
|
||
|
|
||
|
// Note that we divide by 2 below; we want to round down
|
||
|
psp->ppT = GAlloc(psp->cp/2 * sizeof(LPVOID));
|
||
|
if (!psp->ppT)
|
||
|
return FALSE;
|
||
|
|
||
|
PAMergeSort2(psp, 0, psp->cp);
|
||
|
GFree(psp->ppT);
|
||
|
return TRUE;
|
||
|
}
|
||
|
#endif // MERGESORT
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Sort the array.
|
||
|
|
||
|
Returns:
|
||
|
Cond: --
|
||
|
*/
|
||
|
BOOL PUBLIC PASort(
|
||
|
HPA ppa,
|
||
|
PFNPACOMPARE pfnCmp,
|
||
|
LPARAM lParam)
|
||
|
{
|
||
|
SORTPARAMS sp;
|
||
|
|
||
|
sp.cp = ppa->cp;
|
||
|
sp.pp = ppa->pp;
|
||
|
sp.pfnCmp = pfnCmp;
|
||
|
sp.lParam = lParam;
|
||
|
|
||
|
#ifdef USEQUICKSORT
|
||
|
return PAQuickSort(&sp);
|
||
|
#endif
|
||
|
#ifdef USEHEAPSORT
|
||
|
return PAHeapSort(&sp);
|
||
|
#endif
|
||
|
#ifdef MERGESORT
|
||
|
return PAMergeSort(&sp);
|
||
|
#endif
|
||
|
}
|
||
|
|
||
|
|
||
|
/*----------------------------------------------------------
|
||
|
Purpose: Search for pFind in array.
|
||
|
|
||
|
Returns:
|
||
|
Cond: --
|
||
|
*/
|
||
|
DWORD PUBLIC PASearch(
|
||
|
HPA ppa,
|
||
|
PVOID pFind,
|
||
|
DWORD iStart,
|
||
|
PFNPACOMPARE pfnCompare,
|
||
|
LPARAM lParam,
|
||
|
UINT options)
|
||
|
{
|
||
|
DWORD cp = PAGetCount(ppa);
|
||
|
|
||
|
ASSERT(pfnCompare);
|
||
|
ASSERT(PA_ERR != iStart);
|
||
|
|
||
|
// Only allow these wierd flags if the list is sorted
|
||
|
ASSERT((options & PAS_SORTED) || !(options & (PAS_INSERTBEFORE | PAS_INSERTAFTER)));
|
||
|
|
||
|
if (!(options & PAS_SORTED))
|
||
|
{
|
||
|
// Not sorted: do linear search.
|
||
|
DWORD i;
|
||
|
|
||
|
for (i = iStart; i < cp; i++)
|
||
|
{
|
||
|
if (0 == pfnCompare(pFind, PAFastGetPtr(ppa, i), lParam))
|
||
|
return i;
|
||
|
}
|
||
|
return PA_ERR;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Search the array using binary search. If several adjacent
|
||
|
// elements match the target element, the index of the first
|
||
|
// matching element is returned.
|
||
|
|
||
|
DWORD iRet = PA_ERR; // assume no match
|
||
|
BOOL bFound = FALSE;
|
||
|
int nCmp = 0;
|
||
|
DWORD iLow = 0; // Don't bother using iStart for binary search
|
||
|
DWORD iMid = 0;
|
||
|
|
||
|
if (0 < cp)
|
||
|
{
|
||
|
DWORD iHigh = cp - 1;
|
||
|
|
||
|
// (OK for cp == 0)
|
||
|
while (iLow <= iHigh)
|
||
|
{
|
||
|
iMid = (iLow + iHigh) / 2;
|
||
|
|
||
|
nCmp = pfnCompare(pFind, PAFastGetPtr(ppa, iMid), lParam);
|
||
|
|
||
|
if (0 > nCmp)
|
||
|
{
|
||
|
// Account for the fact we are working with
|
||
|
// unsigned values
|
||
|
if (0 == iMid)
|
||
|
break;
|
||
|
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, PAFastGetPtr(ppa, 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 & (PAS_INSERTAFTER | PAS_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 & (PAS_INSERTAFTER | PAS_INSERTBEFORE)) )
|
||
|
{
|
||
|
// Sanity check with linear search
|
||
|
ASSERT(PASearch(ppa, pFind, iStart, pfnCompare, lParam, options & ~PAS_SORTED) == iRet);
|
||
|
}
|
||
|
return iRet;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#endif // NODA
|
||
|
|
||
|
#endif // NOMEM
|