Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2089 lines
43 KiB

//
// 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