diff options
Diffstat (limited to 'private/unimodem/common/rovmem.c')
-rw-r--r-- | private/unimodem/common/rovmem.c | 2269 |
1 files changed, 2269 insertions, 0 deletions
diff --git a/private/unimodem/common/rovmem.c b/private/unimodem/common/rovmem.c new file mode 100644 index 000000000..653cb1859 --- /dev/null +++ b/private/unimodem/common/rovmem.c @@ -0,0 +1,2269 @@ +// +// 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 <rovcomm.h> + +#ifndef NOMEM + +////////////////////////////////////////////////////////////////// + +#ifdef WINNT + +/*---------------------------------------------------------- +Purpose: Wide-char version of SetStringA + +Returns: TRUE on success +Cond: -- +*/ +BOOL PUBLIC SetStringW( + LPWSTR FAR * ppszBuf, + LPCWSTR psz) // NULL to free *ppszBuf + { + BOOL bRet = FALSE; + + ASSERT(ppszBuf); + + // Free the buffer? + if (!psz) + { + // Yes + if (*ppszBuf) + { + LocalFree(LOCALOF(*ppszBuf)); + *ppszBuf = NULL; + } + bRet = TRUE; + } + else + { + // No; (re)allocate and set buffer + UINT cb = CbFromCchW(lstrlenW(psz)+1); + + if (*ppszBuf) + { + // Need to reallocate? + if (cb > LocalSize(LOCALOF(*ppszBuf))) + { + // Yes + LPWSTR pszT = (LPWSTR)LocalReAlloc(LOCALOF(*ppszBuf), cb, LMEM_MOVEABLE | LMEM_ZEROINIT); + if (pszT) + { + *ppszBuf = pszT; + bRet = TRUE; + } + } + else + { + // No + bRet = TRUE; + } + } + else + { + *ppszBuf = (LPWSTR)LocalAlloc(LPTR, cb); + if (*ppszBuf) + { + bRet = TRUE; + } + } + + if (bRet) + { + ASSERT(*ppszBuf); + lstrcpyW(*ppszBuf, psz); + } + } + return bRet; + } + + +/*---------------------------------------------------------- +Purpose: Wide-char version of CatStringA + +Returns: TRUE on success +Cond: -- +*/ +BOOL +PRIVATE +MyCatStringW( + IN OUT LPWSTR FAR * ppszBuf, + IN LPCWSTR psz, OPTIONAL + IN BOOL bMultiString) + { + BOOL bRet = FALSE; + + ASSERT(ppszBuf); + + // Free the buffer? + if ( !psz ) + { + // Yes + if (*ppszBuf) + { + LocalFree(LOCALOF(*ppszBuf)); + *ppszBuf = NULL; + } + bRet = TRUE; + } + else + { + // No; (re)allocate and set buffer + LPWSTR pszBuf = *ppszBuf; + UINT cch; + + cch = lstrlenW(psz) + 1; // account for null + + if (bMultiString) + { + cch++; // account for second null + } + + if (pszBuf) + { + UINT cchExisting; + LPWSTR pszT; + + // Figure out how much of the buffer has been used + + // Is this a multi-string (one with strings with a double-null + // terminator)? + if (bMultiString) + { + // Yes + UINT cchT; + + cchExisting = 0; + pszT = (LPWSTR)pszBuf; + while (0 != *pszT) + { + cchT = lstrlenW(pszT) + 1; + cchExisting += cchT; + pszT += cchT; + } + } + else + { + // No; (don't need to count null because it is already + // counted in cch) + cchExisting = lstrlenW(pszBuf); + } + + // Need to reallocate? + if (CbFromCchW(cch + cchExisting) > LocalSize(LOCALOF(pszBuf))) + { + // Yes; realloc at least MAX_BUF to cut down on the amount + // of calls in the future + cch = cchExisting + max(cch, MAX_BUF); + + pszT = (LPWSTR)LocalReAlloc(LOCALOF(pszBuf), + CbFromCchW(cch), + LMEM_MOVEABLE | LMEM_ZEROINIT); + if (pszT) + { + pszBuf = pszT; + *ppszBuf = pszBuf; + bRet = TRUE; + } + } + else + { + // No + bRet = TRUE; + } + + pszBuf += cchExisting; + } + else + { + cch = max(cch, MAX_BUF); + + pszBuf = (LPWSTR)LocalAlloc(LPTR, CbFromCchW(cch)); + if (pszBuf) + { + bRet = TRUE; + } + + *ppszBuf = pszBuf; + } + + if (bRet) + { + ASSERT(pszBuf); + + lstrcpyW(pszBuf, psz); + + if (bMultiString) + { + pszBuf[lstrlenW(psz) + 1] = 0; // Add second null terminator + } + } + } + + return bRet; + } + + +/*---------------------------------------------------------- +Purpose: Wide-char version of CatStringA + +Returns: TRUE on success +Cond: -- +*/ +BOOL +PUBLIC +CatStringW( + IN OUT LPWSTR FAR * ppszBuf, + IN LPCWSTR psz) + { + return MyCatStringW(ppszBuf, psz, FALSE); + } + +/*---------------------------------------------------------- +Purpose: Wide-char version of CatMultiStringA + +Returns: TRUE on success +Cond: -- +*/ +BOOL +PUBLIC +CatMultiStringW( + IN OUT LPWSTR FAR * ppszBuf, + IN LPCWSTR psz) + { + return MyCatStringW(ppszBuf, psz, TRUE); + } + +#endif // WINNT + + +/*---------------------------------------------------------- +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 SetStringA( + LPSTR FAR * ppszBuf, + LPCSTR psz) // NULL to free *ppszBuf + { + BOOL bRet = FALSE; + + ASSERT(ppszBuf); + + // Free the buffer? + if (!psz) + { + // Yes + if (ppszBuf) + { + LocalFree(LOCALOF(*ppszBuf)); + *ppszBuf = NULL; + } + bRet = TRUE; + } + else + { + // No; (re)allocate and set buffer + UINT cb = CbFromCchA(lstrlenA(psz)+1); + + if (*ppszBuf) + { + // Need to reallocate? + if (cb > LocalSize(LOCALOF(*ppszBuf))) + { + // Yes + LPSTR pszT = (LPSTR)LocalReAlloc(LOCALOF(*ppszBuf), cb, LMEM_MOVEABLE | LMEM_ZEROINIT); + if (pszT) + { + *ppszBuf = pszT; + bRet = TRUE; + } + } + else + { + // No + bRet = TRUE; + } + } + else + { + *ppszBuf = (LPSTR)LocalAlloc(LPTR, cb); + if (*ppszBuf) + { + bRet = TRUE; + } + } + + if (bRet) + { + ASSERT(*ppszBuf); + lstrcpyA(*ppszBuf, psz); + } + } + return bRet; + } + + +/*---------------------------------------------------------- +Purpose: Concatenates psz onto *ppszBuf. Will alloc or + realloc *ppszBuf accordingly. + + If bMultiString is TRUE, psz will be appended with + a null terminator separating the existing string + and new string. A double-null terminator will + be tacked on the end, too. + + To free, call MyCatString(ppszBuf, NULL). + +Returns: TRUE on success +Cond: -- +*/ +BOOL +PRIVATE +MyCatStringA( + IN OUT LPSTR FAR * ppszBuf, + IN LPCSTR psz, OPTIONAL + IN BOOL bMultiString) + { + BOOL bRet = FALSE; + + ASSERT(ppszBuf); + + // Free the buffer? + if ( !psz ) + { + // Yes + if (*ppszBuf) + { + LocalFree(LOCALOF(*ppszBuf)); + *ppszBuf = NULL; + } + bRet = TRUE; + } + else + { + // No; (re)allocate and set buffer + LPSTR pszBuf = *ppszBuf; + UINT cch; + + cch = lstrlenA(psz) + 1; // account for null + + if (bMultiString) + { + cch++; // account for second null + } + + if (pszBuf) + { + UINT cchExisting; + LPSTR pszT; + + // Figure out how much of the buffer has been used + + // Is this a multi-string (one with strings with a double-null + // terminator)? + if (bMultiString) + { + // Yes + UINT cchT; + + cchExisting = 0; + pszT = (LPSTR)pszBuf; + while (0 != *pszT) + { + cchT = lstrlenA(pszT) + 1; + cchExisting += cchT; + pszT += cchT; + } + } + else + { + // No; (don't need to count null because it is already + // counted in cch) + cchExisting = lstrlenA(pszBuf); + } + + // Need to reallocate? + if (CbFromCchA(cch + cchExisting) > LocalSize(LOCALOF(pszBuf))) + { + // Yes; realloc at least MAX_BUF to cut down on the amount + // of calls in the future + cch = cchExisting + max(cch, MAX_BUF); + + pszT = (LPSTR)LocalReAlloc(LOCALOF(pszBuf), + CbFromCchA(cch), + LMEM_MOVEABLE | LMEM_ZEROINIT); + if (pszT) + { + pszBuf = pszT; + *ppszBuf = pszBuf; + bRet = TRUE; + } + } + else + { + // No + bRet = TRUE; + } + + pszBuf += cchExisting; + } + else + { + cch = max(cch, MAX_BUF); + + pszBuf = (LPSTR)LocalAlloc(LPTR, CbFromCchA(cch)); + if (pszBuf) + { + bRet = TRUE; + } + + *ppszBuf = pszBuf; + } + + if (bRet) + { + ASSERT(pszBuf); + + lstrcpyA(pszBuf, psz); + + if (bMultiString) + { + pszBuf[lstrlenA(psz) + 1] = 0; // Add second null terminator + } + } + } + return bRet; + } + + +/*---------------------------------------------------------- +Purpose: Concatenates psz onto *ppszBuf. Will alloc or + realloc *ppszBuf accordingly. + + To free, call CatString(ppszBuf, NULL). + +Returns: TRUE on success +Cond: -- +*/ +BOOL +PUBLIC +CatStringA( + IN OUT LPSTR FAR * ppszBuf, + IN LPCSTR psz) OPTIONAL + { + return MyCatStringA(ppszBuf, psz, FALSE); + } + + +/*---------------------------------------------------------- +Purpose: Concatenates psz onto *ppszBuf. Will alloc or + realloc *ppszBuf accordingly. + + psz will be appended with a null terminator separating + the existing string and new string. A double-null + terminator will be tacked on the end, too. + + To free, call CatMultiString(ppszBuf, NULL). + +Returns: TRUE on success +Cond: -- +*/ +BOOL +PUBLIC +CatMultiStringA( + IN OUT LPSTR FAR * ppszBuf, + IN LPCSTR psz) + { + return MyCatStringA(ppszBuf, psz, TRUE); + } + + + +////////////////////////////////////////////////////////////////// + + +#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; + + TRACE_MSG(TF_GENERAL, "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("", on) // back to default optimizations + +#ifndef NOSHAREDHEAP + +#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; + + // BUGBUG, 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(LOCALOF(pb), (int)cb, LMEM_MOVEABLE | LMEM_ZEROINIT); + if (!pbNew) + cbOld = LocalSize(LOCALOF(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(LOCALOF(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(LOCALOF(pb)); + + _asm { + pop ds + } + + return (DWORD)wSize; +} + +#pragma optimize("", on) + +#endif // NOSHAREDHEAP + +////////////////////////////////////////////////////////////////// + +#else // WIN32 +// +// Win32 memory management wrappers +// + +// +// Shared heap memory management +// +#if !defined(NOSHAREDHEAP) && defined(WIN95) + +// 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 SharedTerminate() + { + // Assuming that everything else has exited + // + if (g_hSharedHeap != NULL) + HeapDestroy(g_hSharedHeap); + g_hSharedHeap = NULL; + } + + +/*---------------------------------------------------------- +Purpose: Allocate out of shared heap + +Returns: Pointer to allocate memory +Cond: -- +*/ +void FAR * 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 FAR * PUBLIC SharedReAlloc( + LPVOID 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( + LPVOID 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( + LPVOID 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( + LPTSTR FAR * ppszBuf, + LPCTSTR psz) // NULL to free *ppszBuf + { + BOOL bRet; + + ASSERT(ppszBuf); + + // Free the buffer? + if (!psz) + { + // Yes + if (ppszBuf) + SharedFree(*ppszBuf); + bRet = TRUE; + } + else + { + // No; (re)allocate and set buffer + DWORD cb = CbFromCch(lstrlen(psz)+1); + + LPTSTR pszT = SharedReAlloc(*ppszBuf, cb); + if (pszT) + { + *ppszBuf = pszT; + lstrcpy(*ppszBuf, psz); + bRet = TRUE; + } + else + bRet = FALSE; + } + return bRet; + } +#endif // NOSHAREDHEAP + +#endif // WIN32 + +////////////////////////////////////////////////////////////////// + + +#ifndef NODA + +#ifdef WIN32 + +// +// 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 HeapSize(hheap, 0, pb); + else + return GGetSize(pb); + } + +#endif // WIN32 + + +/*---------------------------------------------------------- +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 + { + LPVOID FAR * pp; + PFNPACOMPARE pfnCmp; + LPARAM lParam; + int cp; +#ifdef MERGESORT + LPVOID FAR * ppT; +#endif + } SORTPARAMS; + +BOOL NEAR PAQuickSort(SORTPARAMS FAR* psp); +BOOL NEAR PAQuickSort2(int i, int j, SORTPARAMS FAR* psp); +BOOL NEAR PAHeapSort(SORTPARAMS FAR* psp); +void NEAR PAHeapSortPushDown(int first, int last, SORTPARAMS FAR* psp); +BOOL NEAR PAMergeSort(SORTPARAMS FAR* psp); +void NEAR PAMergeSort2(SORTPARAMS FAR* psp, int iFirst, int cItems); + + +// +// Structure Array +// + +typedef struct _SA + { + // NOTE: The following field MUST be defined at the beginning of the + // structure in order for SAGetCount() to work. + int cItem; // number of elements in sa + + LPVOID aItem; // memory for elements + int cItemAlloc; // number items which fit in aItem + int cbItem; // size of each item + int cItemGrow; // number items to grow cItemAlloc by + DWORD dwFlags; + HANDLE hheap; + +#ifdef DEBUG + UINT magic; +#endif + } SA; + +#define SA_PITEM(psa, index) ((LPVOID)(((BYTE FAR*)(psa)->aItem) + ((index) * (psa)->cbItem))) + + +/*---------------------------------------------------------- +Purpose: Create a structure array. + +Returns: handle to structure array +Cond: -- +*/ +HSA PUBLIC SACreate( + int cbItem, + int cItemGrow, + HANDLE hheap, // Must be non-NULL if SAF_HEAP set + DWORD dwFlags) + { + HSA psa; + + ASSERT(cbItem); + ASSERT(hheap && IsFlagSet(dwFlags, SAF_HEAP) || + IsFlagClear(dwFlags, SAF_HEAP)); + + psa = PrvAlloc(dwFlags, hheap, sizeof(SA)); + +#if !defined(NOSHAREDHEAP) && defined(WIN95) + if (IsFlagSet(dwFlags, PAF_SHARED)) + hheap = g_hSharedHeap; +#endif + + if (psa) + { + psa->cItem = 0; + psa->cItemAlloc = 0; + psa->cbItem = cbItem; + psa->cItemGrow = (cItemGrow == 0 ? 1 : cItemGrow); + psa->aItem = NULL; + psa->dwFlags = dwFlags; + psa->hheap = hheap; + +#ifdef DEBUG + psa->magic = SA_MAGIC; +#endif + } + return psa; + } + + +/*---------------------------------------------------------- +Purpose: Destroys a structure array. + +Returns: +Cond: -- +*/ +BOOL PUBLIC SADestroy( + HSA psa) + { + ASSERT(IsSA(psa)); + + if (psa == NULL) // allow NULL for low memory cases, still assert + return TRUE; + +#ifdef DEBUG + psa->cItem = 0; + psa->cItemAlloc = 0; + psa->cbItem = 0; + psa->magic = 0; +#endif + if (psa->aItem && !MemFree(psa->hheap, psa->aItem)) + return FALSE; + + MemFree(psa->hheap, psa); + return TRUE; + } + + +/*---------------------------------------------------------- +Purpose: Copy structure at index into buffer. + +Returns: TRUE + FALSE +Cond: -- +*/ +BOOL PUBLIC SAGetItem( + HSA psa, + int index, + LPVOID pitem) + { + ASSERT(IsSA(psa)); + ASSERT(pitem); + + if (index < 0 || index >= psa->cItem) + { + TRACE_MSG(TF_ERROR, "SA: Invalid index: %d", index); + return FALSE; + } + + hmemcpy(pitem, SA_PITEM(psa, index), psa->cbItem); + return TRUE; + } + + +/*---------------------------------------------------------- +Purpose: Get pointer to structure in array + +Returns: +Cond: -- +*/ +LPVOID PUBLIC SAGetItemPtr( + HSA psa, + int index) + { + ASSERT(IsSA(psa)); + + if (index < 0 || index >= psa->cItem) + { + TRACE_MSG(TF_ERROR, "SA: Invalid index: %d", index); + return NULL; + } + return SA_PITEM(psa, index); + } + + +/*---------------------------------------------------------- +Purpose: Set item + +Returns: +Cond: -- +*/ +BOOL PUBLIC SASetItem( + HSA psa, + int index, + LPVOID pitem) + { + ASSERT(pitem); + ASSERT(IsSA(psa)); + + if (index < 0) + { + TRACE_MSG(TF_ERROR, "SA: Invalid index: %d", index); + return FALSE; + } + + if (index >= psa->cItem) + { + if (index + 1 > psa->cItemAlloc) + { + int cItemAlloc = (((index + 1) + psa->cItemGrow - 1) / psa->cItemGrow) * psa->cItemGrow; + + LPVOID 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: +Returns: +Cond: -- +*/ +int PUBLIC SAInsertItem( + HSA psa, + int index, + LPVOID pitem) + { + ASSERT(pitem); + ASSERT(IsSA(psa)); + + if (index < 0) + { + TRACE_MSG(TF_ERROR, "SA: Invalid index: %d", index); + return -1; + } + + if (index > psa->cItem) + index = psa->cItem; + + if (psa->cItem + 1 > psa->cItemAlloc) + { + LPVOID aItemNew = MemReAlloc(psa->hheap, psa->aItem, + (psa->cItemAlloc + psa->cItemGrow) * psa->cbItem); + if (!aItemNew) + return -1; + + psa->aItem = aItemNew; + psa->cItemAlloc += psa->cItemGrow; + } + + 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); + + return index; + } + + +/*---------------------------------------------------------- +Purpose: +Returns: +Cond: -- +*/ +BOOL PUBLIC SADeleteItem( + HSA psa, + int index) + { + ASSERT(IsSA(psa)); + + if (index < 0 || index >= psa->cItem) + { + TRACE_MSG(TF_ERROR, "SA: Invalid index: %d", 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) + { + LPVOID 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. +// + int cp; + LPVOID FAR * pp; + + HANDLE hheap; // Heap to allocate from if NULL use shared + + int cpAlloc; + int cpGrow; + DWORD dwFlags; + +#ifdef DEBUG + UINT magic; +#endif +} PA; + + + +/*---------------------------------------------------------- +Purpose: Creates a pointer array. + +Returns: handle to pointer array + NULL (if out of memory) + +Cond: -- +*/ +HPA PUBLIC PACreate( + int cpGrow, + HANDLE hheap, // Must be non-null if PAF_HEAP set + DWORD dwFlags) // PAF_* + { + HPA ppa; + + ASSERT(hheap && IsFlagSet(dwFlags, SAF_HEAP) || + IsFlagClear(dwFlags, SAF_HEAP)); + + ppa = PrvAlloc(dwFlags, hheap, sizeof(PA)); + +#if !defined(NOSHAREDHEAP) && defined(WIN95) + if (IsFlagSet(dwFlags, PAF_SHARED)) + hheap = g_hSharedHeap; +#endif + + 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 + } + return 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) + { + int i = PAGetCount(ppa) - 1; + + for (; 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 + +Returns: pointer to new array + NULL (if out of memory) + +Cond: -- +*/ +HPA PUBLIC PAClone( + HPA ppa, + HPA ppaNew) + { + BOOL fAlloc = FALSE; + + if (!ppaNew) + { + ppaNew = PACreate(ppa->cpGrow, ppa->hheap, ppa->dwFlags); + if (!ppaNew) + return NULL; + + fAlloc = TRUE; + } + + if (!PAGrow(ppaNew, ppa->cpAlloc)) + { + if (fAlloc) + PADestroy(ppaNew); + return NULL; + } + + ppaNew->cp = ppa->cp; + hmemcpy(ppaNew->pp, ppa->pp, ppa->cp * sizeof(LPVOID)); + + return ppaNew; + } + + +/*---------------------------------------------------------- +Purpose: Get a pointer stored in index + +Returns: stored pointer +Cond: -- +*/ +LPVOID PUBLIC PAGetPtr( + HPA ppa, + int index) + { + ASSERT(IsPA(ppa)); + + if (index < 0 || index >= ppa->cp) + return NULL; + + return ppa->pp[index]; + } + + +/*---------------------------------------------------------- +Purpose: Gets the index that pointer p is stored at + +Returns: index +Cond: -- +*/ +int PUBLIC PAGetPtrIndex( + HPA ppa, + LPVOID p) + { + LPVOID FAR * pp; + LPVOID FAR * ppMax; + + ASSERT(IsPA(ppa)); + if (ppa->pp) + { + pp = ppa->pp; + ppMax = pp + ppa->cp; + for ( ; pp < ppMax; pp++) + { + if (*pp == p) + return (pp - ppa->pp); + } + } + return -1; + } + + +/*---------------------------------------------------------- +Purpose: Grow the pointer array + +Returns: +Cond: -- +*/ +BOOL PUBLIC PAGrow( + HPA ppa, + int cpAlloc) + { + ASSERT(IsPA(ppa)); + + if (cpAlloc > ppa->cpAlloc) + { + LPVOID FAR * ppNew; + + cpAlloc = ((cpAlloc + ppa->cpGrow - 1) / ppa->cpGrow) * ppa->cpGrow; + + if (ppa->pp) + ppNew = (LPVOID FAR *)MemReAlloc(ppa->hheap, ppa->pp, cpAlloc * sizeof(LPVOID)); + else + ppNew = (LPVOID FAR *)PrvAlloc(ppa->dwFlags, ppa->hheap, cpAlloc * sizeof(LPVOID)); + 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, + int index, + LPVOID p) + { + ASSERT(IsPA(ppa)); + + if (index < 0) + { + TRACE_MSG(TF_ERROR, "PA: Invalid index: %d", 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 a pointer at index. If index is beyond the + current size of array, this function appends the + pointer to the end of the array. + +Returns: index pointer is stored in + PA_ERR + +Cond: -- +*/ +int PUBLIC PAInsertPtr( + HPA ppa, + int index, + LPVOID p) + { + ASSERT(IsPA(ppa)); + + if (index < 0) + { + TRACE_MSG(TF_ERROR, "PA: Invalid index: %d", index); + return -1; + } + if (index > ppa->cp) + index = ppa->cp; + + // Make sure we have room for one more item + // + if (ppa->cp + 1 > ppa->cpAlloc) + { + if (!PAGrow(ppa, ppa->cp + 1)) + return -1; + } + + // 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(LPVOID)); + } + + ppa->pp[index] = p; + ppa->cp++; + + return index; + } + + +/*---------------------------------------------------------- +Purpose: Delete a pointer from index. + +Returns: the deleted pointer + NULL (if index is out of range) + +Cond: -- +*/ +LPVOID PUBLIC PADeletePtr( + HPA ppa, + int index) + { + LPVOID p; + + ASSERT(IsPA(ppa)); + + if (index < 0 || index >= ppa->cp) + { + TRACE_MSG(TF_ERROR, "PA: Invalid index: %d", 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(LPVOID)); + } + ppa->cp--; + + if (ppa->cpAlloc - ppa->cp > ppa->cpGrow) + { + LPVOID FAR * ppNew; + ppNew = MemReAlloc(ppa->hheap, ppa->pp, (ppa->cpAlloc - ppa->cpGrow) * sizeof(LPVOID)); + + 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) - 1; + + for (; 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; + } + + +/*---------------------------------------------------------- +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 + } + +#ifdef USEQUICKSORT + +BOOL NEAR PAQuickSort(SORTPARAMS FAR* psp) +{ + return PAQuickSort2(0, psp->cp - 1, psp); +} + +BOOL NEAR PAQuickSort2(int i, int j, SORTPARAMS FAR* psp) +{ + LPVOID FAR * pp = psp->pp; + LPARAM lParam = psp->lParam; + PFNPACOMPARE pfnCmp = psp->pfnCmp; + + int iPivot; + LPVOID pFirst; + int k; + int result; + + iPivot = -1; + 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 != -1) + { + int l = i; + int r = j; + LPVOID pivot = pp[iPivot]; + + do + { + LPVOID 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; +} +#endif // USEQUICKSORT + +#ifdef USEHEAPSORT + +void NEAR PAHeapSortPushDown(int first, int last, SORTPARAMS FAR* psp) +{ + LPVOID FAR * pp = psp->pp; + LPARAM lParam = psp->lParam; + PFNPACOMPARE pfnCmp = psp->pfnCmp; + int r; + int r2; + LPVOID p; + + 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) + { + p = pp[r-1]; pp[r-1] = pp[r2-1]; pp[r2-1] = p; + } + break; + } + else + { + int wR2toR21 = (*pfnCmp)(pp[r2-1], pp[r2+1-1], lParam); + + if (wRTo2R < 0 && wR2toR21 >= 0) + { + p = pp[r-1]; pp[r-1] = pp[r2-1]; pp[r2-1] = p; + r = r2; + } + else if ((*pfnCmp)(pp[r-1], pp[r2+1-1], lParam) < 0 && wR2toR21 < 0) + { + p = pp[r-1]; pp[r-1] = pp[r2+1-1]; pp[r2+1-1] = p; + r = r2 + 1; + } + else + { + break; + } + } + } +} + +BOOL NEAR PAHeapSort(SORTPARAMS FAR* psp) +{ + LPVOID FAR * pp = psp->pp; + int c = psp->cp; + int i; + + for (i = c / 2; i >= 1; i--) + PAHeapSortPushDown(i, c, psp); + + for (i = c; i >= 2; i--) + { + LPVOID p = pp[0]; pp[0] = pp[i-1]; pp[i-1] = p; + + 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, int iFirst, int cItems) +{ + // + // Notes: + // This function is separated from PAMergeSort2() to avoid comsuming + // stack variables. Never inline this. + // + int cHalf = cItems/2; + int iIn1, iIn2, iOut; + LPVOID FAR * 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, 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 + 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 = LocalAlloc(LPTR, psp->cp/2 * sizeof(LPVOID)); + if (!psp->ppT) + return FALSE; + + PAMergeSort2(psp, 0, psp->cp); + LocalFree(psp->ppT); + return TRUE; +} +#endif // MERGESORT + +// Search function +// +int PUBLIC PASearch(HPA ppa, LPVOID pFind, int iStart, + PFNPACOMPARE pfnCompare, LPARAM lParam, UINT options) +{ + int cp = PAGetCount(ppa); + + ASSERT(pfnCompare); + ASSERT(0 <= 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. + int i; + + for (i = iStart; i < cp; i++) + { + if (0 == pfnCompare(pFind, PAFastGetPtr(ppa, 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, PAFastGetPtr(ppa, 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, 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 |