|
|
#include "ctlspriv.h"
//
// 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...
//
#define MERGESORT
// #define USEHEAPSORT
#ifdef DEBUG
#define DSA_MAGIC ('S' | ('A' << 8))
#define IsDSA(pdsa) ((pdsa) && (pdsa)->magic == DSA_MAGIC)
#define DPA_MAGIC ('P' | ('A' << 8))
#define IsDPA(pdpa) ((pdpa) && (pdpa)->magic == DPA_MAGIC)
#else
#define IsDSA(pdsa)
#define IsDPA(pdsa)
#endif
typedef struct { void** pp; PFNDPACOMPARE pfnCmp; LPARAM lParam; int cp; #ifdef MERGESORT
void** ppT; #endif
} SORTPARAMS;
BOOL DPA_QuickSort(SORTPARAMS* psp); BOOL DPA_QuickSort2(int i, int j, SORTPARAMS* psp); BOOL DPA_HeapSort(SORTPARAMS* psp); void DPA_HeapSortPushDown(int first, int last, SORTPARAMS* psp); BOOL DPA_MergeSort(SORTPARAMS* psp); void DPA_MergeSort2(SORTPARAMS* psp, int iFirst, int cItems);
//========== Dynamic structure array ====================================
// Dynamic structure array
typedef struct _DSA { // NOTE: The following field MUST be defined at the beginning of the
// structure in order for GetItemCount() to work.
int cItem; // # of elements in dsa
void* aItem; // memory for elements
int cItemAlloc; // # items which fit in aItem
int cbItem; // size of each item
int cItemGrow; // # items to grow cItemAlloc by
#ifdef DEBUG
UINT magic; #endif
} DSA;
#define DSA_PITEM(pdsa, index) ((void*)(((BYTE*)(pdsa)->aItem) + ((index) * (pdsa)->cbItem)))
#ifdef DEBUG
#define BF_ONDAVALIDATE 0x00001000
void DABreakFn(void) { if (IsFlagSet(g_dwBreakFlags, BF_ONDAVALIDATE)) ASSERT(0); }
#define DABreak() DABreakFn()
#else
#define DABreak()
#endif
HDSA WINAPI DSA_Create(int cbItem, int cItemGrow) { HDSA pdsa = Alloc(sizeof(DSA));
ASSERT(cbItem);
if (pdsa) { ASSERT(pdsa->cItem == 0); ASSERT(pdsa->cItemAlloc == 0); pdsa->cbItem = cbItem; pdsa->cItemGrow = (cItemGrow == 0 ? 1 : cItemGrow); ASSERT(pdsa->aItem == NULL); #ifdef DEBUG
pdsa->magic = DSA_MAGIC; #endif
} return pdsa; }
BOOL WINAPI DSA_Destroy(HDSA pdsa) {
if (pdsa == NULL) { // allow NULL for low memory cases
return TRUE; }
// Components rely on not having to check for NULL
ASSERT(IsDSA(pdsa));
#ifdef DEBUG
pdsa->cItem = 0; pdsa->cItemAlloc = 0; pdsa->cbItem = 0; pdsa->magic = 0; #endif
if (pdsa->aItem && !Free(pdsa->aItem)) { return FALSE; }
return Free(pdsa); }
void WINAPI DSA_EnumCallback(HDSA pdsa, PFNDSAENUMCALLBACK pfnCB, void *pData) { int i; if (!pdsa) return; ASSERT(IsDSA(pdsa));
for (i = 0; i < pdsa->cItem; i++) { if (!pfnCB(DSA_GetItemPtr(pdsa, i), pData)) break; } }
void WINAPI DSA_DestroyCallback(HDSA pdsa, PFNDSAENUMCALLBACK pfnCB, void *pData) { DSA_EnumCallback(pdsa, pfnCB, pData); DSA_Destroy(pdsa); }
BOOL WINAPI DSA_GetItem(HDSA pdsa, int index, void* pitem) { ASSERT(IsDSA(pdsa)); ASSERT(pitem);
if (index < 0 || index >= pdsa->cItem) { #ifdef DEBUG
// Don't assert if index == pdsa->cItems as some clients simply want to walk the list and no need to call getcount...
if (index != pdsa->cItem) { DebugMsg(DM_ERROR, TEXT("DSA: GetItem: Invalid index: %d"), index); DABreak(); } #endif
return FALSE; }
CopyMemory(pitem, DSA_PITEM(pdsa, index), pdsa->cbItem); return TRUE; }
void* WINAPI DSA_GetItemPtr(HDSA pdsa, int index) { ASSERT(IsDSA(pdsa));
if (index < 0 || index >= pdsa->cItem) { #ifdef DEBUG
// Don't assert if index == pdsa->cItems as some clients simply want to walk the list and no need to call getcount...
if (index != pdsa->cItem) { DebugMsg(DM_ERROR, TEXT("DSA: GetItemPtr: Invalid index: %d"), index); // DABreak(); // caller knows
} #endif
return NULL; } return DSA_PITEM(pdsa, index); }
BOOL DSA_ForceGrow(HDSA pdsa, int iNumberToAdd) { ASSERT(IsDSA(pdsa));
if (!pdsa) return FALSE;
if (pdsa->cItem + iNumberToAdd > pdsa->cItemAlloc) { int cItemAlloc = (((pdsa->cItemAlloc + iNumberToAdd) + pdsa->cItemGrow - 1) / pdsa->cItemGrow) * pdsa->cItemGrow;
void* aItemNew = ReAlloc(pdsa->aItem, cItemAlloc * pdsa->cbItem); if (!aItemNew) { return FALSE; }
pdsa->aItem = aItemNew; pdsa->cItemAlloc = cItemAlloc; } return TRUE; }
BOOL WINAPI DSA_SetItem(HDSA pdsa, int index, void* pitem) { ASSERT(pitem); ASSERT(IsDSA(pdsa));
if (index < 0) { DebugMsg(DM_ERROR, TEXT("DSA: SetItem: Invalid index: %d"), index); DABreak(); return FALSE; }
if (index >= pdsa->cItem) { if (index + 1 > pdsa->cItemAlloc) { int cItemAlloc = (((index + 1) + pdsa->cItemGrow - 1) / pdsa->cItemGrow) * pdsa->cItemGrow;
void* aItemNew = ReAlloc(pdsa->aItem, cItemAlloc * pdsa->cbItem); if (!aItemNew) { return FALSE; }
pdsa->aItem = aItemNew; pdsa->cItemAlloc = cItemAlloc; } pdsa->cItem = index + 1; }
CopyMemory(DSA_PITEM(pdsa, index), pitem, pdsa->cbItem);
return TRUE; }
int WINAPI DSA_InsertItem(HDSA pdsa, int index, void* pitem) { ASSERT(pitem); ASSERT(IsDSA(pdsa));
if (index < 0) { DebugMsg(DM_ERROR, TEXT("DSA: InsertItem: Invalid index: %d"), index); DABreak(); return -1; }
if (index > pdsa->cItem) index = pdsa->cItem;
if (pdsa->cItem + 1 > pdsa->cItemAlloc) { void* aItemNew = ReAlloc(pdsa->aItem, (pdsa->cItemAlloc + pdsa->cItemGrow) * pdsa->cbItem); if (!aItemNew) { return -1; }
pdsa->aItem = aItemNew; pdsa->cItemAlloc += pdsa->cItemGrow; }
if (index < pdsa->cItem) { MoveMemory(DSA_PITEM(pdsa, index + 1), DSA_PITEM(pdsa, index), (pdsa->cItem - index) * pdsa->cbItem); } pdsa->cItem++; MoveMemory(DSA_PITEM(pdsa, index), pitem, pdsa->cbItem);
return index; }
BOOL WINAPI DSA_DeleteItem(HDSA pdsa, int index) { ASSERT(IsDSA(pdsa));
if (index < 0 || index >= pdsa->cItem) { DebugMsg(DM_ERROR, TEXT("DSA: DeleteItem: Invalid index: %d"), index); DABreak(); return FALSE; }
if (index < pdsa->cItem - 1) { MoveMemory(DSA_PITEM(pdsa, index), DSA_PITEM(pdsa, index + 1), (pdsa->cItem - (index + 1)) * pdsa->cbItem); } pdsa->cItem--;
if (pdsa->cItemAlloc - pdsa->cItem > pdsa->cItemGrow) { void* aItemNew = ReAlloc(pdsa->aItem, (pdsa->cItemAlloc - pdsa->cItemGrow) * pdsa->cbItem); if (aItemNew) { pdsa->aItem = aItemNew; } else { // If the shrink fails, then just continue with the old (slightly
// too big) allocation. Go ahead and let cItemAlloc decrease
// so we don't keep trying to realloc smaller
} pdsa->cItemAlloc -= pdsa->cItemGrow; } return TRUE; }
BOOL WINAPI DSA_DeleteAllItems(HDSA pdsa) { ASSERT(IsDSA(pdsa));
if (pdsa->aItem && !Free(pdsa->aItem)) { return FALSE; }
pdsa->aItem = NULL; pdsa->cItem = pdsa->cItemAlloc = 0; return TRUE; }
//================== Dynamic pointer array implementation ===========
typedef struct _DPA { // NOTE: The following two fields MUST be defined in this order, at
// the beginning of the structure in order for the macro APIs to work.
int cp; void** pp;
HANDLE hheap; // Heap to allocate from if NULL use shared
int cpAlloc; int cpGrow; #ifdef DEBUG
UINT magic; #endif
} DPA;
HDPA WINAPI DPA_Create(int cpGrow) { return DPA_CreateEx(cpGrow, NULL); }
// Should nuke the standard DPA above...
HDPA WINAPI DPA_CreateEx(int cpGrow, HANDLE hheap) { HDPA pdpa; if (hheap == NULL) { hheap = GetProcessHeap(); pdpa = ControlAlloc(hheap, sizeof(DPA)); } else pdpa = ControlAlloc(hheap, sizeof(DPA)); if (pdpa) { ASSERT(pdpa->cp == 0); ASSERT(pdpa->cpAlloc == 0); pdpa->cpGrow = (cpGrow < 8 ? 8 : cpGrow); ASSERT(pdpa->pp == NULL); pdpa->hheap = hheap; #ifdef DEBUG
pdpa->magic = DPA_MAGIC; #endif
} return pdpa; }
BOOL WINAPI DPA_Destroy(HDPA pdpa) { if (pdpa == NULL) // allow NULL for low memory cases, still assert
return TRUE;
ASSERT(IsDPA(pdpa)); ASSERT(pdpa->hheap);
#ifdef DEBUG
pdpa->cp = 0; pdpa->cpAlloc = 0; pdpa->magic = 0; #endif
if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp)) return FALSE;
return ControlFree(pdpa->hheap, pdpa); }
HDPA WINAPI DPA_Clone(HDPA pdpa, HDPA pdpaNew) { BOOL fAlloc = FALSE;
if (!pdpaNew) { pdpaNew = DPA_CreateEx(pdpa->cpGrow, pdpa->hheap); if (!pdpaNew) { return NULL; }
fAlloc = TRUE; }
if (!DPA_Grow(pdpaNew, pdpa->cpAlloc)) { if (!fAlloc) { DPA_Destroy(pdpaNew); } return NULL; }
pdpaNew->cp = pdpa->cp; CopyMemory(pdpaNew->pp, pdpa->pp, pdpa->cp * sizeof(void*));
return pdpaNew; }
void* WINAPI DPA_GetPtr(HDPA pdpa, INT_PTR index) { ASSERT(IsDPA(pdpa));
if (!pdpa || index < 0 || index >= pdpa->cp) return NULL;
return pdpa->pp[index]; }
int WINAPI DPA_GetPtrIndex(HDPA pdpa, void* p) { void** pp; void** ppMax;
ASSERT(IsDPA(pdpa)); if (pdpa && pdpa->pp) { pp = pdpa->pp; ppMax = pp + pdpa->cp; for ( ; pp < ppMax; pp++) { if (*pp == p) return (int) (pp - pdpa->pp); } } return -1; }
BOOL WINAPI DPA_Grow(HDPA pdpa, int cpAlloc) { ASSERT(IsDPA(pdpa));
if (!pdpa) return FALSE;
if (cpAlloc > pdpa->cpAlloc) { void** ppNew;
cpAlloc = ((cpAlloc + pdpa->cpGrow - 1) / pdpa->cpGrow) * pdpa->cpGrow;
if (pdpa->pp) ppNew = (void**)ControlReAlloc(pdpa->hheap, pdpa->pp, cpAlloc * sizeof(void*)); else ppNew = (void**)ControlAlloc(pdpa->hheap, cpAlloc * sizeof(void*)); if (!ppNew) return FALSE;
pdpa->pp = ppNew; pdpa->cpAlloc = cpAlloc;
//
// Grow more agressively as we get bigger, up to a maximum of
// 512 at a time. Note, we'll only hit our outer bound growth
// at a time limit once we've already got that many items in the
// DPA anyway...
//
if (pdpa->cpGrow < 256) { pdpa->cpGrow = pdpa->cpGrow << 1; } } return TRUE; }
BOOL WINAPI DPA_SetPtr(HDPA pdpa, int index, void* p) { ASSERT(IsDPA(pdpa));
if (!pdpa) return FALSE;
if (index < 0) { DebugMsg(DM_ERROR, TEXT("DPA: SetPtr: Invalid index: %d"), index); DABreak(); return FALSE; }
if (index >= pdpa->cp) { if (!DPA_Grow(pdpa, index + 1)) return FALSE; // If we grew by more than one, must zero-init all the stuff in the middle
ZeroMemory(pdpa->pp + pdpa->cp, sizeof(void *) * (index - pdpa->cp)); pdpa->cp = index + 1; }
pdpa->pp[index] = p;
return TRUE; }
int WINAPI DPA_InsertPtr(HDPA pdpa, int index, void* p) { ASSERT(IsDPA(pdpa));
if (!pdpa) return -1;
if (index < 0) { DebugMsg(DM_ERROR, TEXT("DPA: InsertPtr: Invalid index: %d"), index); DABreak(); return -1; } if (index > pdpa->cp) index = pdpa->cp;
// Make sure we have room for one more item
//
if (pdpa->cp + 1 > pdpa->cpAlloc) { if (!DPA_Grow(pdpa, pdpa->cp + 1)) return -1; }
// If we are inserting, we need to slide everybody up
//
if (index < pdpa->cp) { MoveMemory(&pdpa->pp[index + 1], &pdpa->pp[index], (pdpa->cp - index) * sizeof(void*)); }
pdpa->pp[index] = p; pdpa->cp++;
return index; }
void* WINAPI DPA_DeletePtr(HDPA pdpa, int index) { void* p;
ASSERT(IsDPA(pdpa));
if (!pdpa) return FALSE;
if (index < 0 || index >= pdpa->cp) { DebugMsg(DM_ERROR, TEXT("DPA: DeltePtr: Invalid index: %d"), index); DABreak(); return NULL; }
p = pdpa->pp[index];
if (index < pdpa->cp - 1) { MoveMemory(&pdpa->pp[index], &pdpa->pp[index + 1], (pdpa->cp - (index + 1)) * sizeof(void*)); } pdpa->cp--;
if (pdpa->cpAlloc - pdpa->cp > pdpa->cpGrow) { void** ppNew; ppNew = ControlReAlloc(pdpa->hheap, pdpa->pp, (pdpa->cpAlloc - pdpa->cpGrow) * sizeof(void*));
if (ppNew) pdpa->pp = ppNew; else { // If the shrink fails, then just continue with the old (slightly
// too big) allocation. Go ahead and let cpAlloc decrease
// so we don't keep trying to realloc smaller
} pdpa->cpAlloc -= pdpa->cpGrow; } return p; }
BOOL WINAPI DPA_DeleteAllPtrs(HDPA pdpa) { if (!pdpa) return FALSE;
ASSERT(IsDPA(pdpa));
if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp)) return FALSE; pdpa->pp = NULL; pdpa->cp = pdpa->cpAlloc = 0; return TRUE; }
void WINAPI DPA_EnumCallback(HDPA pdpa, PFNDPAENUMCALLBACK pfnCB, void *pData) { int i; if (!pdpa) return; ASSERT(IsDPA(pdpa));
for (i = 0; i < pdpa->cp; i++) { if (!pfnCB(DPA_FastGetPtr(pdpa, i), pData)) break; } }
void WINAPI DPA_DestroyCallback(HDPA pdpa, PFNDPAENUMCALLBACK pfnCB, void *pData) { DPA_EnumCallback(pdpa, pfnCB, pData); DPA_Destroy(pdpa); }
typedef struct _DPASTREAMHEADER { DWORD cbSize; // Size of entire stream
DWORD dwVersion; // For versioning
int celem; } DPASTREAMHEADER;
#define DPASTREAM_VERSION 1
/*----------------------------------------------------------
Purpose: Saves the DPA to a stream by writing out a header, and then calling the given callback to write each element.
The callback can end the write early by returning something other than S_OK. Returning an error will cancel the entire write. Returning S_FALSE will stop the write.
Returns: S_OK or S_FALSE for success. S_FALSE only if callback stops early errors */ HRESULT WINAPI DPA_SaveStream( IN HDPA pdpa, IN PFNDPASTREAM pfn, IN IStream * pstm, IN void * pvInstData) { HRESULT hres = E_INVALIDARG;
if (IS_VALID_HANDLE(pdpa, DPA) && IS_VALID_CODE_PTR(pstm, IStream *) && IS_VALID_CODE_PTR(pfn, PFNDPASTREAM)) { DPASTREAMHEADER header; LARGE_INTEGER dlibMove = { 0 }; ULARGE_INTEGER ulPosBegin;
// Get the current seek position, so we can update the header
// once we know how much we've written
hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &ulPosBegin); if (SUCCEEDED(hres)) { // Write the header (we will update some of this once we're
// finished)
header.cbSize = 0; header.dwVersion = DPASTREAM_VERSION; header.celem = 0;
// First write out the header
hres = pstm->lpVtbl->Write(pstm, &header, sizeof(header), NULL);
if (SUCCEEDED(hres)) { DPASTREAMINFO info; int cel = DPA_GetPtrCount(pdpa); void **ppv = DPA_GetPtrPtr(pdpa);
// This keeps the count of what is actually written
info.iPos = 0;
// Write each element
for (; 0 < cel; cel--, ppv++) { info.pvItem = *ppv; hres = pfn(&info, pstm, pvInstData);
// Returning S_FALSE from callback means it didn't
// write anything for this element, so don't increment
// the iPos (which refers to the count written).
if (S_OK == hres) info.iPos++; else if (FAILED(hres)) { hres = S_FALSE; break; } }
if (FAILED(hres)) { // Reposition pointer to beginning
dlibMove.LowPart = ulPosBegin.LowPart; dlibMove.HighPart = ulPosBegin.HighPart; pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL); } else { ULARGE_INTEGER ulPosEnd;
// Calculate how much was written
hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &ulPosEnd); if (SUCCEEDED(hres)) { // We only save the low part
ASSERT(ulPosEnd.HighPart == ulPosBegin.HighPart);
// Update the header
header.celem = info.iPos; header.cbSize = ulPosEnd.LowPart - ulPosBegin.LowPart;
dlibMove.LowPart = ulPosBegin.LowPart; dlibMove.HighPart = ulPosBegin.HighPart; pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL); pstm->lpVtbl->Write(pstm, &header, sizeof(header), NULL);
// Reposition pointer
dlibMove.LowPart = ulPosEnd.LowPart; dlibMove.HighPart = ulPosEnd.HighPart; pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL); } } } } }
return hres; }
/*----------------------------------------------------------
Purpose: Loads the DPA from a stream by calling the given callback to read each element.
The callback can end the read early by returning something other than S_OK.
Returns: S_OK on success S_FALSE if the callback aborted early or the stream ended abruptly. DPA is partially filled. error on anything else */ HRESULT WINAPI DPA_LoadStream( OUT HDPA * ppdpa, IN PFNDPASTREAM pfn, IN IStream * pstm, IN void * pvInstData) { HRESULT hres = E_INVALIDARG;
if (IS_VALID_WRITE_PTR(ppdpa, HDPA) && IS_VALID_CODE_PTR(pstm, IStream *) && IS_VALID_CODE_PTR(pfn, PFNDPASTREAM)) { DPASTREAMHEADER header; LARGE_INTEGER dlibMove = { 0 }; ULARGE_INTEGER ulPosBegin; ULONG cbRead;
*ppdpa = NULL;
// Get the current seek position so we can position pointer
// correctly upon error.
hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &ulPosBegin); if (SUCCEEDED(hres)) { // Read the header
hres = pstm->lpVtbl->Read(pstm, &header, sizeof(header), &cbRead); if (SUCCEEDED(hres)) { if (sizeof(header) > cbRead || sizeof(header) > header.cbSize || DPASTREAM_VERSION != header.dwVersion) { hres = E_FAIL; } else { // Create the list
HDPA pdpa = DPA_Create(header.celem); if ( !pdpa || !DPA_Grow(pdpa, header.celem)) hres = E_OUTOFMEMORY; else { // Read each element
DPASTREAMINFO info; void **ppv = DPA_GetPtrPtr(pdpa);
for (info.iPos = 0; info.iPos < header.celem; ) { info.pvItem = NULL; hres = pfn(&info, pstm, pvInstData);
// Returning S_FALSE from the callback means
// it skipped this stream element.
// Don't increment iPos (which refers to the
// count read).
if (S_OK == hres) { *ppv = info.pvItem;
info.iPos++; ppv++; } else if (FAILED(hres)) { hres = S_FALSE; break; } }
pdpa->cp = info.iPos; *ppdpa = pdpa; } }
// Reposition pointer if we failed
if (S_OK != hres) { if (S_FALSE == hres) { // Position pointer to the end
dlibMove.LowPart = ulPosBegin.LowPart + header.cbSize; } else { // Position pointer to beginning
dlibMove.LowPart = ulPosBegin.LowPart; } dlibMove.HighPart = ulPosBegin.HighPart; pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL); } } }
ASSERT(SUCCEEDED(hres) && *ppdpa || FAILED(hres) && NULL == *ppdpa); }
return hres; }
/*----------------------------------------------------------
Purpose: Merge two DPAs. This takes two arrays and merges the source array into the destination.
Merge options:
DPAM_SORTED The arrays are already sorted; don't sort DPAM_UNION The resulting array is the union of all elements in both arrays. DPAM_INTERSECT Only elements in the source array that intersect with the dest array are merged. DPAM_NORMAL Like DPAM_INTERSECT except the dest array also maintains its original, additional elements.
Returns: S_OK for success. errors if merge fails
Cond: -- */ BOOL WINAPI DPA_Merge( IN HDPA pdpaDest, IN HDPA pdpaSrc, IN DWORD dwFlags, IN PFNDPACOMPARE pfnCompare, IN PFNDPAMERGE pfnMerge, IN LPARAM lParam) { BOOL bRet = FALSE;
if (IS_VALID_HANDLE(pdpaSrc, DPA) && IS_VALID_HANDLE(pdpaDest, DPA) && IS_VALID_CODE_PTR(pfnCompare, PFNDPACOMPARE) && IS_VALID_CODE_PTR(pfnMerge, PFNDPAMERGE)) { int iSrc; int iDest; int nCmp; void **ppvSrc; void **ppvDest;
bRet = TRUE;
// Are the arrays already sorted?
if ( !(dwFlags & DPAM_SORTED) ) { // No; sort them
DPA_Sort(pdpaSrc, pfnCompare, lParam); DPA_Sort(pdpaDest, pfnCompare, lParam); }
// This merges in-place. The size of the resulting DPA
// depends on the options:
//
// DPAM_NORMAL Same size as the dest DPA before
// the merge.
//
// DPAM_UNION Min size is the larger of the two.
// Max size is the sum of the two.
//
// DPAM_INTERSECT Min size is zero.
// Max size is the smaller of the two.
//
// We iterate backwards to minimize the amount of moves we
// incur by calling DPA_DeletePtr.
//
iSrc = pdpaSrc->cp - 1; iDest = pdpaDest->cp - 1; ppvSrc = &DPA_FastGetPtr(pdpaSrc, iSrc); ppvDest = &DPA_FastGetPtr(pdpaDest, iDest);
while (0 <= iSrc && 0 <= iDest) { void *pv;
nCmp = pfnCompare(*ppvDest, *ppvSrc, lParam);
if (0 == nCmp) { // Elements match; merge them.
pv = pfnMerge(DPAMM_MERGE, *ppvDest, *ppvSrc, lParam); if (NULL == pv) { bRet = FALSE; break; } *ppvDest = pv;
iSrc--; ppvSrc--; iDest--; ppvDest--; } else if (0 < nCmp) { // pvSrc < pvDest. The source array doesn't have pvDest.
if (dwFlags & DPAM_INTERSECT) { // Delete pvDest
pfnMerge(DPAMM_DELETE, DPA_DeletePtr(pdpaDest, iDest), NULL, lParam); } else { ; // Keep it (do nothing)
}
// Move onto the next element in the dest array
iDest--; ppvDest--; } else { // pvSrc > pvDest. The dest array doesn't have pvSrc.
if (dwFlags & DPAM_UNION) { // Add pvSrc
pv = pfnMerge(DPAMM_INSERT, *ppvSrc, NULL, lParam); if (NULL == pv) { bRet = FALSE; break; }
DPA_InsertPtr(pdpaDest, iDest+1, pv); // DPA_InsertPtr may end up reallocating the pointer array
// thus making ppvDest invalid
ppvDest = &DPA_FastGetPtr(pdpaDest, iDest); } else { ; // Skip it (do nothing)
}
// Move onto the next element in the source array
iSrc--; ppvSrc--; } } // there are some items left in src
if ((dwFlags & DPAM_UNION) && 0 <= iSrc) { for (; 0 <= iSrc; iSrc--, ppvSrc--) { void *pv = pfnMerge(DPAMM_INSERT, *ppvSrc, NULL, lParam); if (NULL == pv) { bRet = FALSE; break; } DPA_InsertPtr(pdpaDest, 0, pv); } } }
return bRet; }
BOOL WINAPI DPA_Sort(HDPA pdpa, PFNDPACOMPARE pfnCmp, LPARAM lParam) { SORTPARAMS sp;
sp.cp = pdpa->cp; sp.pp = pdpa->pp; sp.pfnCmp = pfnCmp; sp.lParam = lParam;
#ifdef USEQUICKSORT
return DPA_QuickSort(&sp); #endif
#ifdef USEHEAPSORT
return DPA_HeapSort(&sp); #endif
#ifdef MERGESORT
return DPA_MergeSort(&sp); #endif
}
#ifdef USEQUICKSORT
BOOL DPA_QuickSort(SORTPARAMS* psp) { return DPA_QuickSort2(0, psp->cp - 1, psp); }
BOOL DPA_QuickSort2(int i, int j, SORTPARAMS* psp) { void** pp = psp->pp; LPARAM lParam = psp->lParam; PFNDPACOMPARE pfnCmp = psp->pfnCmp;
int iPivot; void* 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; void* pivot = pp[iPivot];
do { void* 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) DPA_QuickSort2(i, l - 1, psp); if (j > l) DPA_QuickSort2(l, j, psp); } return TRUE; } #endif // USEQUICKSORT
#ifdef USEHEAPSORT
void DPA_HeapSortPushDown(int first, int last, SORTPARAMS* psp) { void** pp = psp->pp; LPARAM lParam = psp->lParam; PFNDPACOMPARE pfnCmp = psp->pfnCmp; int r; int r2; void* 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 DPA_HeapSort(SORTPARAMS* psp) { void** pp = psp->pp; int c = psp->cp; int i;
for (i = c / 2; i >= 1; i--) DPA_HeapSortPushDown(i, c, psp);
for (i = c; i >= 2; i--) { void* p = pp[0]; pp[0] = pp[i-1]; pp[i-1] = p;
DPA_HeapSortPushDown(1, i - 1, psp); } return TRUE; } #endif // USEHEAPSORT
#if defined(MERGESORT)
#define SortCompare(psp, pp1, i1, pp2, i2) \
(psp->pfnCmp(pp1[i1], pp2[i2], psp->lParam))
//
// This function merges two sorted lists and makes one sorted list.
// psp->pp[iFirst, iFirst+cItes/2-1], psp->pp[iFirst+cItems/2, iFirst+cItems-1]
//
void DPA_MergeThem(SORTPARAMS* psp, int iFirst, int cItems) { //
// Notes:
// This function is separated from DPA_MergeSort2() to avoid comsuming
// stack variables. Never inline this.
//
int cHalf = cItems/2; int iIn1, iIn2, iOut; void **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
CopyMemory(psp->ppT, ppvSrc, cHalf * sizeof(void*));
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
CopyMemory(&ppvSrc[iOut], &psp->ppT[iIn1], (cItems-iOut)*sizeof(void *)); break; } } } }
//
// This function sorts a give list (psp->pp[iFirst,iFirst-cItems-1]).
//
void DPA_MergeSort2(SORTPARAMS* psp, int iFirst, int cItems) { //
// Notes:
// This function is recursively called. Therefore, we should minimize
// the number of local variables and parameters. At this point, we
// use one local variable and three parameters.
//
int cHalf;
switch(cItems) { case 1: return;
case 2: // Swap them, if they are out of order.
if (SortCompare(psp, psp->pp, iFirst, psp->pp, iFirst+1) > 0) { psp->ppT[0] = psp->pp[iFirst]; psp->pp[iFirst] = psp->pp[iFirst+1]; psp->pp[iFirst+1] = psp->ppT[0]; } break;
default: cHalf = cItems/2;
// Sort each half
DPA_MergeSort2(psp, iFirst, cHalf); DPA_MergeSort2(psp, iFirst+cHalf, cItems-cHalf); // Then, merge them.
DPA_MergeThem(psp, iFirst, cItems); break; } }
BOOL DPA_MergeSort(SORTPARAMS* psp) { if (psp->cp==0) return TRUE;
// Note that we divide by 2 below; we want to round down
psp->ppT = LocalAlloc(LPTR, psp->cp/2 * sizeof(void *)); if (!psp->ppT) return FALSE;
DPA_MergeSort2(psp, 0, psp->cp); LocalFree(psp->ppT); return TRUE; } #endif // MERGESORT
// Search function
//
int WINAPI DPA_Search(HDPA pdpa, void* pFind, int iStart, PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT options) { int cp = DPA_GetPtrCount(pdpa);
ASSERT(pfnCompare); ASSERT(0 <= iStart);
// Only allow these wierd flags if the list is sorted
ASSERT((options & DPAS_SORTED) || !(options & (DPAS_INSERTBEFORE | DPAS_INSERTAFTER)));
if (!(options & DPAS_SORTED)) { // Not sorted: do linear search.
int i;
for (i = iStart; i < cp; i++) { if (0 == pfnCompare(pFind, DPA_FastGetPtr(pdpa, i), lParam)) return i; } return -1; } else { // Search the array using binary search. If several adjacent
// elements match the target element, the index of the first
// matching element is returned.
int iRet = -1; // assume no match
BOOL bFound = FALSE; int nCmp = 0; int iLow = 0; // Don't bother using iStart for binary search
int iMid = 0; int iHigh = cp - 1;
// (OK for cp == 0)
while (iLow <= iHigh) { iMid = (iLow + iHigh) / 2;
nCmp = pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid), lParam);
if (0 > nCmp) iHigh = iMid - 1; // First is smaller
else if (0 < nCmp) iLow = iMid + 1; // First is larger
else { // Match; search back for first match
bFound = TRUE; while (0 < iMid) { if (0 != pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid-1), lParam)) break; else iMid--; } break; } }
if (bFound) { ASSERT(0 <= iMid); iRet = iMid; }
// Did the search fail AND
// is one of the strange search flags set?
if (!bFound && (options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE))) { // Yes; return the index where the target should be inserted
// if not found
if (0 < nCmp) // First is larger
iRet = iLow; else iRet = iMid; // (We don't distinguish between the two flags anymore)
} else if ( !(options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE)) ) { // Sanity check with linear search
ASSERT(DPA_Search(pdpa, pFind, iStart, pfnCompare, lParam, options & ~DPAS_SORTED) == iRet); } return iRet; } }
//===========================================================================
//
// String ptr management routines
//
// Copy as much of *psz to *pszBuf as will fit
//
// Warning: this same code is duplicated below.
//
int WINAPI Str_GetPtr(LPCTSTR pszCurrent, LPTSTR pszBuf, int cchBuf) { int cchToCopy;
if (!pszCurrent) { ASSERT(FALSE); if (cchBuf > 0) { *pszBuf = TEXT('\0'); }
return 0; }
cchToCopy = lstrlen(pszCurrent);
// if pszBuf is NULL, or they passed cchBuf = 0, return the needed buff size
if (!pszBuf || !cchBuf) { return cchToCopy + 1; } if (cchToCopy >= cchBuf) { cchToCopy = cchBuf - 1; }
CopyMemory(pszBuf, pszCurrent, cchToCopy * sizeof(TCHAR)); pszBuf[cchToCopy] = TEXT('\0');
return cchToCopy + 1; }
#ifdef DEBUG
//
// Str_GetPtr0 is just like Str_GetPtr except that it doesn't assert if
// pszCurrent = NULL.
//
int WINAPI Str_GetPtr0(LPCTSTR pszCurrent, LPTSTR pszBuf, int cchBuf) { return Str_GetPtr(pszCurrent ? pszCurrent : c_szNULL, pszBuf, cchBuf); } #endif
//
// If we are build Unicode, then this is the ANSI version
// of the above function.
//
int WINAPI Str_GetPtrA(LPCSTR pszCurrent, LPSTR pszBuf, int cchBuf) { int cchToCopy;
if (!pszCurrent) { ASSERT(FALSE);
if (cchBuf > 0) { *pszBuf = '\0'; }
return 0; }
cchToCopy = lstrlenA(pszCurrent);
// if pszBuf is NULL, or they passed cchBuf = 0, return the needed buff size
if (!pszBuf || !cchBuf) { return cchToCopy + 1; } if (cchToCopy >= cchBuf) { cchToCopy = cchBuf - 1; }
CopyMemory(pszBuf, pszCurrent, cchToCopy * sizeof(CHAR)); pszBuf[cchToCopy] = TEXT('\0');
return cchToCopy + 1; }
//
// This function is not exported.
//
BOOL Str_Set(LPTSTR *ppsz, LPCTSTR psz) { if (!psz || (psz == LPSTR_TEXTCALLBACK)) { if (*ppsz) { if (*ppsz != (LPSTR_TEXTCALLBACK)) LocalFree(*ppsz); } *ppsz = (LPTSTR)psz; } else { LPTSTR pszNew = *ppsz; UINT cbNew = (lstrlen(psz) + 1) * sizeof(TCHAR);
if (pszNew == LPSTR_TEXTCALLBACK) { pszNew = NULL; } pszNew = CCLocalReAlloc(pszNew, cbNew);
if (!pszNew) { return FALSE; }
StringCbCopy(pszNew, cbNew, psz); *ppsz = pszNew; } return TRUE; }
// Set *ppszCurrent to a copy of pszNew, and free the previous value, if necessary
//
// WARNING: This same code is duplicated below
//
BOOL WINAPI Str_SetPtr(LPTSTR * ppszCurrent, LPCTSTR pszNew) { LPTSTR pszOld; LPTSTR pszNewCopy = NULL;
if (pszNew) { DWORD cchNewCopy = lstrlen(pszNew)+1;
// alloc a new buffer w/ room for the null terminator
pszNewCopy = (LPTSTR) Alloc(cchNewCopy * sizeof(TCHAR));
if (!pszNewCopy) { return FALSE; }
StringCchCopy(pszNewCopy, cchNewCopy, pszNew); } pszOld = InterlockedExchangePointer((void **)ppszCurrent, pszNewCopy);
if (pszOld) { Free(pszOld); }
return TRUE; }
//
// ANSI stub when built Unicode.
//
BOOL WINAPI Str_SetPtrA(LPSTR * ppszCurrent, LPCSTR pszNew) { LPSTR pszOld; LPSTR pszNewCopy = NULL;
if (pszNew) { DWORD cchNewCopy = lstrlenA(pszNew)+1;
// alloc a new buffer w/ room for the null terminator
pszNewCopy = (LPSTR) Alloc(cchNewCopy * sizeof(CHAR));
if (!pszNewCopy) { return FALSE; }
StringCchCopyA(pszNewCopy, cchNewCopy, pszNew); }
pszOld = InterlockedExchangePointer((void **)ppszCurrent, pszNewCopy);
if (pszOld) { Free(pszOld); }
return TRUE; }
|