Leaked source code of windows server 2003
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
45 KiB

  1. //
  2. // Copyright (c) Microsoft Corporation 1993-1995
  3. //
  4. // mem.c
  5. //
  6. // This file contains memory management and dynamic
  7. // array functions.
  8. //
  9. // History:
  10. // 09-27-94 ScottH Taken from commctrl
  11. // 04-29-95 ScottH Taken from briefcase and cleaned up
  12. //
  13. #include "proj.h"
  14. #include "common.h"
  15. #ifndef NOMEM
  16. //////////////////////////////////////////////////////////////////
  17. #ifndef WIN32
  18. //
  19. // Subsegment Allocation for 16-bit
  20. //
  21. #define MAX_WORD 0xffff
  22. DECLARE_HANDLE(HHEAP);
  23. typedef struct
  24. { // maps to the bottom of a 16bit DS
  25. WORD reserved[8];
  26. WORD cAlloc;
  27. WORD cbAllocFailed;
  28. HHEAP hhpFirst;
  29. HHEAP hhpNext;
  30. } HEAP;
  31. #define PHEAP(hhp) ((HEAP FAR*)MAKELP(hhp, 0))
  32. #define MAKEHP(sel, off) ((void _huge*)MAKELP((sel), (off)))
  33. #define CBSUBALLOCMAX 0x0000f000L
  34. HHEAP g_hhpFirst = NULL;
  35. BOOL NEAR DestroyHeap(HHEAP hhp);
  36. void Mem_Terminate()
  37. {
  38. while (g_hhpFirst)
  39. DestroyHeap(g_hhpFirst);
  40. }
  41. BOOL NEAR CreateHeap(WORD cbInitial)
  42. {
  43. HHEAP hhp;
  44. if (cbInitial < 1024)
  45. cbInitial = 1024;
  46. hhp = (HHEAP)GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_SHARE, cbInitial);
  47. if (!hhp)
  48. return FALSE;
  49. if (!LocalInit((WORD)hhp, sizeof(HEAP), cbInitial - 1))
  50. {
  51. GlobalFree(hhp);
  52. return FALSE;
  53. }
  54. PHEAP(hhp)->cAlloc = 0;
  55. PHEAP(hhp)->cbAllocFailed = MAX_WORD;
  56. PHEAP(hhp)->hhpNext = g_hhpFirst;
  57. g_hhpFirst = hhp;
  58. DebugMsg(DM_TRACE, "CreateHeap: added new local heap %x", hhp);
  59. return TRUE;
  60. }
  61. #pragma optimize("o", off) // linked list removals don't optimize correctly
  62. BOOL NEAR DestroyHeap(HHEAP hhp)
  63. {
  64. ASSERT(hhp);
  65. ASSERT(g_hhpFirst);
  66. if (g_hhpFirst == hhp)
  67. {
  68. g_hhpFirst = PHEAP(hhp)->hhpNext;
  69. }
  70. else
  71. {
  72. HHEAP hhpT = g_hhpFirst;
  73. while (PHEAP(hhpT)->hhpNext != hhp)
  74. {
  75. hhpT = PHEAP(hhpT)->hhpNext;
  76. if (!hhpT)
  77. return FALSE;
  78. }
  79. PHEAP(hhpT)->hhpNext = PHEAP(hhp)->hhpNext;
  80. }
  81. if (GlobalFree((HGLOBAL)hhp) != NULL)
  82. return FALSE;
  83. return TRUE;
  84. }
  85. #pragma optimize("", on) // back to default optimizations
  86. #pragma optimize("lge", off) // Suppress warnings associated with use of _asm...
  87. void NEAR* NEAR HeapAlloc(HHEAP hhp, WORD cb)
  88. {
  89. void NEAR* pb;
  90. _asm {
  91. push ds
  92. mov ds,hhp
  93. }
  94. pb = (void NEAR*)LocalAlloc(LMEM_FIXED | LMEM_ZEROINIT, cb);
  95. if (pb)
  96. ((HEAP NEAR*)0)->cAlloc++;
  97. _asm {
  98. pop ds
  99. }
  100. return pb;
  101. }
  102. #pragma optimize("o", off) // linked list removals don't optimize correctly
  103. void _huge* WINAPI SharedAlloc(long cb)
  104. {
  105. void NEAR* pb;
  106. HHEAP hhp;
  107. HHEAP hhpPrev;
  108. // If this is a big allocation, just do a global alloc.
  109. //
  110. if (cb > CBSUBALLOCMAX)
  111. {
  112. void FAR* lpb = MAKEHP(GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT | GMEM_SHARE, cb), 0);
  113. if (!lpb)
  114. DebugMsg(DM_ERROR, "Alloc: out of memory");
  115. return lpb;
  116. }
  117. hhp = g_hhpFirst;
  118. while (TRUE)
  119. {
  120. if (hhp == NULL)
  121. {
  122. if (!CreateHeap(0))
  123. {
  124. DebugMsg(DM_ERROR, "Alloc: out of memory");
  125. return NULL;
  126. }
  127. hhp = g_hhpFirst;
  128. }
  129. pb = HeapAlloc(hhp, (WORD)cb);
  130. if (pb)
  131. return MAKEHP(hhp, pb);
  132. // Record the size of the allocation that failed.
  133. // Later attempts to allocate more than this amount
  134. // will not succeed. This gets reset anytime anything
  135. // is freed in the heap.
  136. //
  137. PHEAP(hhp)->cbAllocFailed = (WORD)cb;
  138. // First heap is full... see if there's room in any other heap...
  139. //
  140. for (hhpPrev = hhp; hhp = PHEAP(hhp)->hhpNext; hhpPrev = hhp)
  141. {
  142. // If the last allocation to fail in this heap
  143. // is not larger than cb, don't even try an allocation.
  144. //
  145. if ((WORD)cb >= PHEAP(hhp)->cbAllocFailed)
  146. continue;
  147. pb = HeapAlloc(hhp, (WORD)cb);
  148. if (pb)
  149. {
  150. // This heap had room: move it to the front...
  151. //
  152. PHEAP(hhpPrev)->hhpNext = PHEAP(hhp)->hhpNext;
  153. PHEAP(hhp)->hhpNext = g_hhpFirst;
  154. g_hhpFirst = hhp;
  155. return MAKEHP(hhp, pb);
  156. }
  157. else
  158. {
  159. // The alloc failed. Set cbAllocFailed...
  160. //
  161. PHEAP(hhp)->cbAllocFailed = (WORD)cb;
  162. }
  163. }
  164. }
  165. }
  166. #pragma optimize("", on) // back to default optimizations
  167. #pragma optimize("lge", off) // Suppress warnings associated with use of _asm...
  168. void _huge* WINAPI SharedReAlloc(void _huge* pb, long cb)
  169. {
  170. void NEAR* pbNew;
  171. void _huge* lpbNew;
  172. UINT cbOld;
  173. // does not work with cb > 64k
  174. if (!pb)
  175. return SharedAlloc(cb);
  176. if (OFFSETOF(pb) == 0)
  177. return MAKEHP(GlobalReAlloc((HGLOBAL)SELECTOROF(pb), cb, GMEM_MOVEABLE | GMEM_ZEROINIT), 0);
  178. _asm {
  179. push ds
  180. mov ds,word ptr [pb+2]
  181. }
  182. pbNew = (void NEAR*)LocalReAlloc((HLOCAL)OFFSETOF(pb), (int)cb, LMEM_MOVEABLE | LMEM_ZEROINIT);
  183. if (!pbNew)
  184. cbOld = LocalSize((HLOCAL)OFFSETOF(pb));
  185. _asm {
  186. pop ds
  187. }
  188. if (pbNew)
  189. return MAKEHP(SELECTOROF(pb), pbNew);
  190. lpbNew = SharedAlloc(cb);
  191. if (lpbNew)
  192. {
  193. hmemcpy((void FAR*)lpbNew, (void FAR*)pb, cbOld);
  194. Free(pb);
  195. }
  196. else
  197. {
  198. DebugMsg(DM_ERROR, "ReAlloc: out of memory");
  199. }
  200. return lpbNew;
  201. }
  202. BOOL WINAPI SharedFree(void _huge* FAR * ppb)
  203. {
  204. BOOL fSuccess;
  205. UINT cAlloc;
  206. void _huge * pb = *ppb;
  207. if (!pb)
  208. return FALSE;
  209. *ppb = 0;
  210. if (OFFSETOF(pb) == 0)
  211. return (GlobalFree((HGLOBAL)SELECTOROF(pb)) == NULL);
  212. _asm {
  213. push ds
  214. mov ds,word ptr [pb+2]
  215. }
  216. fSuccess = (LocalFree((HLOCAL)OFFSETOF(pb)) ? FALSE : TRUE);
  217. cAlloc = 1;
  218. if (fSuccess)
  219. {
  220. cAlloc = --((HEAP NEAR*)0)->cAlloc;
  221. ((HEAP NEAR*)0)->cbAllocFailed = MAX_WORD;
  222. }
  223. _asm {
  224. pop ds
  225. }
  226. if (cAlloc == 0)
  227. DestroyHeap((HHEAP)SELECTOROF(pb));
  228. return fSuccess;
  229. }
  230. DWORD WINAPI SharedGetSize(void _huge* pb)
  231. {
  232. WORD wSize;
  233. if (OFFSETOF(pb) == 0)
  234. return GlobalSize((HGLOBAL)SELECTOROF(pb));
  235. _asm {
  236. push ds
  237. mov ds,word ptr [pb+2]
  238. }
  239. wSize = LocalSize((HLOCAL)OFFSETOF(pb));
  240. _asm {
  241. pop ds
  242. }
  243. return (DWORD)wSize;
  244. }
  245. #pragma optimize("", on)
  246. //////////////////////////////////////////////////////////////////
  247. #else // WIN32
  248. //
  249. // Win32 memory management wrappers
  250. //
  251. // Define a Global Shared Heap that we use to allocate memory
  252. // out of that we need to share between multiple instances.
  253. //
  254. static HANDLE g_hSharedHeap = NULL;
  255. #define MAXHEAPSIZE 2097152
  256. #define HEAP_SHARED 0x04000000 /* put heap in shared memory */
  257. /*----------------------------------------------------------
  258. Purpose: Clean up heap. This function should be called at
  259. the program's termination.
  260. Returns: --
  261. Cond: --
  262. */
  263. void PUBLIC Mem_Terminate()
  264. {
  265. // Assuming that everything else has exited
  266. //
  267. if (g_hSharedHeap != NULL)
  268. HeapDestroy(g_hSharedHeap);
  269. g_hSharedHeap = NULL;
  270. }
  271. /*----------------------------------------------------------
  272. Purpose: Copies psz into *ppszBuf. Will alloc or realloc *ppszBuf
  273. accordingly.
  274. If psz is NULL, this function frees *ppszBuf. This is
  275. the preferred method of freeing the allocated buffer.
  276. Returns: TRUE on success
  277. Cond: --
  278. */
  279. BOOL PUBLIC GSetString(
  280. LPSTR * ppszBuf,
  281. LPCSTR psz) // NULL to free *ppszBuf
  282. {
  283. BOOL bRet = FALSE;
  284. ASSERT(ppszBuf);
  285. // Free the buffer?
  286. if (!psz)
  287. {
  288. // Yes
  289. if (ppszBuf)
  290. {
  291. GFree(*ppszBuf);
  292. *ppszBuf = NULL;
  293. }
  294. bRet = TRUE;
  295. }
  296. else
  297. {
  298. // No; (re)allocate and set buffer
  299. DWORD cb = CbFromCch(lstrlen(psz)+CCH_NUL);
  300. if (*ppszBuf)
  301. {
  302. // Need to reallocate?
  303. if (cb > GGetSize(*ppszBuf))
  304. {
  305. // Yes
  306. LPSTR pszT = GReAlloc(*ppszBuf, cb);
  307. if (pszT)
  308. {
  309. *ppszBuf = pszT;
  310. bRet = TRUE;
  311. }
  312. }
  313. else
  314. {
  315. // No
  316. bRet = TRUE;
  317. }
  318. }
  319. else
  320. {
  321. *ppszBuf = (LPSTR)GAlloc(cb);
  322. if (*ppszBuf)
  323. {
  324. bRet = TRUE;
  325. }
  326. }
  327. if (bRet)
  328. {
  329. ASSERT(*ppszBuf);
  330. lstrcpy(*ppszBuf, psz);
  331. }
  332. }
  333. return bRet;
  334. }
  335. /*----------------------------------------------------------
  336. Purpose: Concatenates psz onto *ppszBuf. Will alloc or
  337. realloc *ppszBuf accordingly.
  338. Returns: TRUE on success
  339. Cond: --
  340. */
  341. BOOL PUBLIC GCatString(
  342. LPSTR * ppszBuf,
  343. LPCSTR psz)
  344. {
  345. BOOL bRet = FALSE;
  346. DWORD cb;
  347. ASSERT(ppszBuf);
  348. ASSERT(psz);
  349. cb = CbFromCch(lstrlen(psz)+CCH_NUL);
  350. if (*ppszBuf)
  351. {
  352. // (Don't need to count nul because it is already counted in cb)
  353. DWORD cbExisting = CbFromCch(lstrlen(*ppszBuf));
  354. // Need to reallocate?
  355. if ((cb+cbExisting) > GGetSize(*ppszBuf))
  356. {
  357. // Yes; realloc at least MAX_BUF to cut down on the amount
  358. // of calls in the future
  359. LPSTR pszT = GReAlloc(*ppszBuf, cbExisting+max(cb, MAX_BUF));
  360. if (pszT)
  361. {
  362. *ppszBuf = pszT;
  363. bRet = TRUE;
  364. }
  365. }
  366. else
  367. {
  368. // No
  369. bRet = TRUE;
  370. }
  371. }
  372. else
  373. {
  374. *ppszBuf = (LPSTR)GAlloc(max(cb, MAX_BUF));
  375. if (*ppszBuf)
  376. {
  377. bRet = TRUE;
  378. }
  379. }
  380. if (bRet)
  381. {
  382. ASSERT(*ppszBuf);
  383. lstrcat(*ppszBuf, psz);
  384. }
  385. return bRet;
  386. }
  387. //
  388. // Shared heap memory management
  389. //
  390. #ifndef NOSHAREDHEAP
  391. /*----------------------------------------------------------
  392. Purpose: Allocate out of shared heap
  393. Returns: Pointer to allocate memory
  394. Cond: --
  395. */
  396. void * PUBLIC SharedAlloc(
  397. DWORD cb)
  398. {
  399. // I will assume that this is the only one that needs the checks to
  400. // see if the heap has been previously created or not
  401. if (g_hSharedHeap == NULL)
  402. {
  403. ENTER_EXCLUSIVE()
  404. {
  405. if (g_hSharedHeap == NULL)
  406. {
  407. g_hSharedHeap = HeapCreate(HEAP_SHARED, 1, MAXHEAPSIZE);
  408. }
  409. }
  410. LEAVE_EXCLUSIVE()
  411. // If still NULL we have problems!
  412. if (g_hSharedHeap == NULL)
  413. return(NULL);
  414. }
  415. return HeapAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, cb);
  416. }
  417. /*----------------------------------------------------------
  418. Purpose: Realloc out of shared heap.
  419. Returns: Possibly new pointer to resized block
  420. Cond: --
  421. */
  422. void * PUBLIC SharedReAlloc(
  423. PVOID pv,
  424. DWORD cb)
  425. {
  426. if (NULL == pv)
  427. {
  428. return SharedAlloc(cb);
  429. }
  430. return HeapReAlloc(g_hSharedHeap, HEAP_ZERO_MEMORY, pv, cb);
  431. }
  432. /*----------------------------------------------------------
  433. Purpose: Free shared memory
  434. Returns: --
  435. Cond: --
  436. */
  437. void PUBLIC _SharedFree(
  438. PVOID pv)
  439. {
  440. ASSERT(pv);
  441. if (pv)
  442. {
  443. HeapFree(g_hSharedHeap, 0, pv);
  444. }
  445. }
  446. /*----------------------------------------------------------
  447. Purpose: Returns the allocated size of a block
  448. Returns: see above
  449. Cond: --
  450. */
  451. DWORD PUBLIC SharedGetSize(
  452. PVOID pv)
  453. {
  454. return HeapSize(g_hSharedHeap, 0, pv);
  455. }
  456. /*----------------------------------------------------------
  457. Purpose: Copies psz into *ppszBuf. Will alloc or realloc *ppszBuf
  458. accordingly.
  459. If psz is NULL, this function frees *ppszBuf. This is
  460. the preferred method of freeing the allocated buffer.
  461. Returns: TRUE on success
  462. Cond: --
  463. */
  464. BOOL PUBLIC SharedSetString(
  465. LPSTR * ppszBuf,
  466. LPCSTR psz) // NULL to free *ppszBuf
  467. {
  468. BOOL bRet;
  469. ASSERT(ppszBuf);
  470. // Free the buffer?
  471. if (!psz)
  472. {
  473. // Yes
  474. if (ppszBuf)
  475. {
  476. SharedFree(*ppszBuf);
  477. *ppszBuf = NULL;
  478. }
  479. bRet = TRUE;
  480. }
  481. else
  482. {
  483. // No; (re)allocate and set buffer
  484. DWORD cb = CbFromCch(lstrlen(psz)+CCH_NUL);
  485. LPSTR pszT = SharedReAlloc(*ppszBuf, cb);
  486. if (pszT)
  487. {
  488. *ppszBuf = pszT;
  489. lstrcpy(*ppszBuf, psz);
  490. bRet = TRUE;
  491. }
  492. else
  493. bRet = FALSE;
  494. }
  495. return bRet;
  496. }
  497. #endif // NOSHAREDHEAP
  498. //
  499. // Memory tracking functions
  500. //
  501. #ifdef DEBUG
  502. typedef struct _HEAPTRACE
  503. {
  504. DWORD cAlloc;
  505. DWORD cFailure;
  506. DWORD cReAlloc;
  507. DWORD cbMaxTotal;
  508. DWORD cCurAlloc;
  509. DWORD cbCurTotal;
  510. } HEAPTRACE;
  511. HEAPTRACE g_htSync = {0}; // Start of zero...
  512. #endif // DEBUG
  513. /*----------------------------------------------------------
  514. Purpose: Allocate from a heap.
  515. Returns: pointer to block of memory
  516. NULL (if out of memory)
  517. Cond: --
  518. */
  519. LPVOID PUBLIC MemAlloc(
  520. HANDLE hheap,
  521. DWORD cb)
  522. {
  523. LPVOID lp;
  524. if (hheap)
  525. {
  526. lp = HeapAlloc(hheap, HEAP_ZERO_MEMORY, cb);
  527. }
  528. else
  529. {
  530. lp = GAlloc(cb);
  531. }
  532. if (lp == NULL)
  533. {
  534. DEBUG_CODE( g_htSync.cFailure++; )
  535. return NULL;
  536. }
  537. #ifdef DEBUG
  538. // Update counts.
  539. g_htSync.cAlloc++;
  540. g_htSync.cCurAlloc++;
  541. g_htSync.cbCurTotal += cb;
  542. if (g_htSync.cbCurTotal > g_htSync.cbMaxTotal)
  543. g_htSync.cbMaxTotal = g_htSync.cbCurTotal;
  544. #endif
  545. return lp;
  546. }
  547. /*----------------------------------------------------------
  548. Purpose: Reallocate a block of memory in a given heap.
  549. Returns: Pointer to reallocated block
  550. NULL (if out of memory)
  551. Cond: --
  552. */
  553. LPVOID PUBLIC MemReAlloc(
  554. HANDLE hheap,
  555. LPVOID pb,
  556. DWORD cb)
  557. {
  558. LPVOID lp;
  559. DEBUG_CODE( DWORD cbOld; )
  560. if (hheap)
  561. {
  562. DEBUG_CODE( cbOld = HeapSize(hheap, 0, pb); )
  563. lp = HeapReAlloc(hheap, HEAP_ZERO_MEMORY, pb, cb);
  564. }
  565. else
  566. {
  567. if (pb)
  568. {
  569. DEBUG_CODE( cbOld = GGetSize(pb); )
  570. lp = GReAlloc(pb, cb);
  571. }
  572. else
  573. {
  574. DEBUG_CODE( cbOld = 0; )
  575. lp = GAlloc(cb);
  576. }
  577. }
  578. if (lp == NULL)
  579. {
  580. DEBUG_CODE( g_htSync.cFailure++; )
  581. return NULL;
  582. }
  583. #ifdef DEBUG
  584. // Update counts.
  585. g_htSync.cReAlloc++;
  586. g_htSync.cbCurTotal += cb - cbOld;
  587. if (g_htSync.cbCurTotal > g_htSync.cbMaxTotal)
  588. g_htSync.cbMaxTotal = g_htSync.cbCurTotal;
  589. #endif
  590. return lp;
  591. }
  592. /*----------------------------------------------------------
  593. Purpose: Free block of memory in heap.
  594. Returns: TRUE
  595. FALSE (if failure)
  596. Cond: --
  597. */
  598. BOOL PUBLIC MemFree(
  599. HANDLE hheap,
  600. LPVOID pb)
  601. {
  602. BOOL fRet;
  603. DEBUG_CODE( DWORD cbOld; )
  604. if (hheap)
  605. {
  606. DEBUG_CODE( cbOld = HeapSize(hheap, 0, pb); )
  607. fRet = HeapFree(hheap, 0, pb);
  608. }
  609. else
  610. {
  611. DEBUG_CODE( cbOld = GGetSize(pb); )
  612. GFree(pb);
  613. fRet = TRUE;
  614. }
  615. #ifdef DEBUG
  616. if (fRet)
  617. {
  618. // Update counts.
  619. g_htSync.cCurAlloc--;
  620. g_htSync.cbCurTotal -= cbOld;
  621. }
  622. #endif
  623. return fRet;
  624. }
  625. /*----------------------------------------------------------
  626. Purpose: Returns the size of the given block.
  627. Returns: size in bytes
  628. Cond: --
  629. */
  630. DWORD PUBLIC MemSize(
  631. HANDLE hheap,
  632. LPVOID pb)
  633. {
  634. if (hheap)
  635. return (DWORD)HeapSize(hheap, 0, pb);
  636. else
  637. return (DWORD)GGetSize(pb);
  638. }
  639. #endif // WIN32
  640. //////////////////////////////////////////////////////////////////
  641. #ifndef NODA
  642. /*----------------------------------------------------------
  643. Purpose: Private alloc for pointer array functions.
  644. Returns: pointer to block of memory
  645. NULL (if out of memory)
  646. Cond: --
  647. */
  648. LPVOID PRIVATE PrvAlloc(
  649. DWORD dwFlags, // PAF_* flags
  650. HANDLE hheap,
  651. DWORD cb)
  652. {
  653. LPVOID lp;
  654. ASSERT(PAF_SHARED == SAF_SHARED);
  655. if (IsFlagSet(dwFlags, PAF_SHARED))
  656. {
  657. lp = SharedAlloc(cb);
  658. }
  659. else
  660. {
  661. lp = MemAlloc(hheap, cb);
  662. }
  663. return lp;
  664. }
  665. // Heapsort is a bit slower, but it doesn't use any stack or memory...
  666. // Mergesort takes a bit of memory (O(n)) and stack (O(log(n)), but very fast...
  667. //
  668. #ifdef WIN32
  669. #define MERGESORT
  670. #else
  671. #define USEHEAPSORT
  672. #endif
  673. #ifdef DEBUG
  674. #define SA_MAGIC ('S' | ('A' << 256))
  675. #define IsSA(psa) ((psa) && (psa)->magic == SA_MAGIC)
  676. #define PA_MAGIC ('P' | ('A' << 256))
  677. #define IsPA(ppa) ((ppa) && (ppa)->magic == PA_MAGIC)
  678. #else
  679. #define IsSA(psa)
  680. #define IsPA(ppa)
  681. #endif
  682. typedef struct
  683. {
  684. PVOID * pp;
  685. PFNPACOMPARE pfnCmp;
  686. LPARAM lParam;
  687. int cp;
  688. #ifdef MERGESORT
  689. PVOID * ppT;
  690. #endif
  691. } SORTPARAMS;
  692. //
  693. // Structure Array
  694. //
  695. typedef struct _SA
  696. {
  697. // NOTE: The following field MUST be defined at the beginning of the
  698. // structure in order for SAGetCount() to work.
  699. DWORD cItem; // number of elements in sa
  700. PVOID aItem; // memory for elements
  701. DWORD cItemAlloc; // number items which fit in aItem
  702. DWORD cbItem; // size of each item
  703. DWORD cItemGrow; // number items to grow cItemAlloc by
  704. DWORD dwFlags;
  705. HANDLE hheap;
  706. #ifdef DEBUG
  707. UINT magic;
  708. #endif
  709. } SA;
  710. #define SA_PITEM(psa, index) ((PVOID)(((char FAR*)(psa)->aItem) + ((index) * (psa)->cbItem)))
  711. /*----------------------------------------------------------
  712. Purpose: Create a structure array.
  713. Returns: TRUE
  714. FALSE (if out of memory or invalid parameters)
  715. Cond: --
  716. */
  717. BOOL PUBLIC SACreateEx(
  718. PHSA phsa,
  719. DWORD cbItem,
  720. DWORD cItemGrow,
  721. HANDLE hheap, // Must be non-NULL if SAF_HEAP set
  722. DWORD dwFlags)
  723. {
  724. HSA psa;
  725. ASSERT(phsa);
  726. ASSERT(0 < cbItem);
  727. psa = PrvAlloc(dwFlags, hheap, sizeof(SA));
  728. if (IsFlagSet(dwFlags, PAF_SHARED))
  729. hheap = g_hSharedHeap;
  730. if (psa)
  731. {
  732. psa->cItem = 0;
  733. psa->cItemAlloc = 0;
  734. psa->cbItem = cbItem;
  735. psa->cItemGrow = (0 == cItemGrow ? 1 : cItemGrow);
  736. psa->aItem = NULL;
  737. psa->dwFlags = dwFlags;
  738. psa->hheap = hheap;
  739. #ifdef DEBUG
  740. psa->magic = SA_MAGIC;
  741. #endif
  742. }
  743. *phsa = psa;
  744. return NULL != psa;
  745. }
  746. /*----------------------------------------------------------
  747. Purpose: Destroys a structure array.
  748. Returns:
  749. Cond: --
  750. */
  751. BOOL PUBLIC SADestroyEx(
  752. HSA psa,
  753. PFNSAFREE pfnFree,
  754. LPARAM lParam)
  755. {
  756. ASSERT(IsSA(psa));
  757. if (psa == NULL) // allow NULL for low memory cases, still assert
  758. return TRUE;
  759. if (psa->aItem)
  760. {
  761. if (pfnFree)
  762. {
  763. DWORD i = SAGetCount(psa);
  764. while (0 < i)
  765. {
  766. i--;
  767. // Caller should not free the actual pointer being
  768. // passed, only the contents!
  769. pfnFree(SA_PITEM(psa, i), lParam);
  770. }
  771. }
  772. if (!MemFree(psa->hheap, psa->aItem))
  773. return FALSE;
  774. }
  775. #ifdef DEBUG
  776. psa->cItem = 0;
  777. psa->cItemAlloc = 0;
  778. psa->cbItem = 0;
  779. psa->magic = 0;
  780. #endif
  781. return MemFree(psa->hheap, psa);
  782. }
  783. /*----------------------------------------------------------
  784. Purpose: Copy structure at index into buffer.
  785. Returns: TRUE
  786. FALSE
  787. Cond: --
  788. */
  789. BOOL PUBLIC SAGetItem(
  790. HSA psa,
  791. DWORD index,
  792. PVOID pitem)
  793. {
  794. ASSERT(IsSA(psa));
  795. ASSERT(pitem);
  796. if (SA_ERR == index || index >= psa->cItem)
  797. {
  798. TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", index);
  799. return FALSE;
  800. }
  801. hmemcpy(pitem, SA_PITEM(psa, index), psa->cbItem);
  802. return TRUE;
  803. }
  804. /*----------------------------------------------------------
  805. Purpose: Get pointer to structure in array
  806. Returns: TRUE (if the index is within range)
  807. Cond: --
  808. */
  809. BOOL PUBLIC SAGetItemPtr(
  810. HSA psa,
  811. DWORD index,
  812. LPVOID * ppv)
  813. {
  814. BOOL bRet;
  815. ASSERT(IsSA(psa));
  816. ASSERT(ppv);
  817. bRet = !(SA_ERR == index || index >= psa->cItem);
  818. if (bRet)
  819. {
  820. *ppv = SA_PITEM(psa, index);
  821. }
  822. else
  823. {
  824. TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", index);
  825. *ppv = NULL;
  826. }
  827. return bRet;
  828. }
  829. /*----------------------------------------------------------
  830. Purpose: Set item
  831. Returns:
  832. Cond: --
  833. */
  834. BOOL PUBLIC SASetItem(
  835. HSA psa,
  836. DWORD index,
  837. PVOID pitem)
  838. {
  839. ASSERT(pitem);
  840. ASSERT(IsSA(psa));
  841. if (SA_ERR == index)
  842. {
  843. TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", index);
  844. return FALSE;
  845. }
  846. if (index >= psa->cItem)
  847. {
  848. if (index + 1 > psa->cItemAlloc)
  849. {
  850. int cItemAlloc = (((index + 1) + psa->cItemGrow - 1) / psa->cItemGrow) * psa->cItemGrow;
  851. PVOID aItemNew = MemReAlloc(psa->hheap, psa->aItem, cItemAlloc * psa->cbItem);
  852. if (!aItemNew)
  853. return FALSE;
  854. psa->aItem = aItemNew;
  855. psa->cItemAlloc = cItemAlloc;
  856. }
  857. psa->cItem = index + 1;
  858. }
  859. hmemcpy(SA_PITEM(psa, index), pitem, psa->cbItem);
  860. return TRUE;
  861. }
  862. /*----------------------------------------------------------
  863. Purpose: Inserts the given item. If *piIndex is greater than
  864. the current size of the array, the item is appended
  865. to the end. Otherwise, the item is inserted at
  866. *piIndex.
  867. If piIndex is NULL, the item is appended to the end.
  868. Use SASetItem to place an item at a specified index,
  869. regardless of the array size.
  870. When this function completes successfully, it sets
  871. *piIndex to the index that the item really gets
  872. inserted at. Otherwise, it sets *piIndex to SA_ERR.
  873. Returns: TRUE (on successful insertion)
  874. FALSE
  875. Cond: --
  876. */
  877. BOOL PUBLIC SAInsertItem(
  878. HSA psa,
  879. LPDWORD pindex, // May be NULL
  880. PVOID pitem)
  881. {
  882. BOOL bRet = TRUE; // assume success
  883. ASSERT(pitem);
  884. ASSERT(IsSA(psa));
  885. if (pindex && SA_ERR == *pindex)
  886. {
  887. TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", *pindex);
  888. bRet = FALSE;
  889. }
  890. else
  891. {
  892. DWORD index;
  893. if (NULL == pindex || *pindex > psa->cItem)
  894. index = psa->cItem;
  895. else
  896. index = *pindex;
  897. if (psa->cItem + 1 > psa->cItemAlloc)
  898. {
  899. PVOID aItemNew = MemReAlloc(psa->hheap, psa->aItem,
  900. (psa->cItemAlloc + psa->cItemGrow) * psa->cbItem);
  901. if (!aItemNew)
  902. bRet = FALSE;
  903. else
  904. {
  905. psa->aItem = aItemNew;
  906. psa->cItemAlloc += psa->cItemGrow;
  907. }
  908. }
  909. if (bRet)
  910. {
  911. // If we are inserting, we need to slide everybody up
  912. if (index < psa->cItem)
  913. {
  914. hmemcpy(SA_PITEM(psa, index + 1), SA_PITEM(psa, index),
  915. (psa->cItem - index) * psa->cbItem);
  916. }
  917. psa->cItem++;
  918. hmemcpy(SA_PITEM(psa, index), pitem, psa->cbItem);
  919. if (pindex)
  920. *pindex = index;
  921. }
  922. else if (pindex)
  923. {
  924. *pindex = SA_ERR;
  925. }
  926. }
  927. return bRet;
  928. }
  929. /*----------------------------------------------------------
  930. Purpose:
  931. Returns:
  932. Cond: --
  933. */
  934. BOOL PUBLIC SADeleteItem(
  935. HSA psa,
  936. DWORD index)
  937. {
  938. ASSERT(IsSA(psa));
  939. if (SA_ERR == index || index >= psa->cItem)
  940. {
  941. TRACE_MSG(TF_ERROR, "SA: Invalid index: %lu", index);
  942. return FALSE;
  943. }
  944. if (index < psa->cItem - 1)
  945. {
  946. hmemcpy(SA_PITEM(psa, index), SA_PITEM(psa, index + 1),
  947. (psa->cItem - (index + 1)) * psa->cbItem);
  948. }
  949. psa->cItem--;
  950. if (psa->cItemAlloc - psa->cItem > psa->cItemGrow)
  951. {
  952. PVOID aItemNew = MemReAlloc(psa->hheap, psa->aItem,
  953. (psa->cItemAlloc - psa->cItemGrow) * psa->cbItem);
  954. ASSERT(aItemNew);
  955. psa->aItem = aItemNew;
  956. psa->cItemAlloc -= psa->cItemGrow;
  957. }
  958. return TRUE;
  959. }
  960. /*----------------------------------------------------------
  961. Purpose:
  962. Returns:
  963. Cond: --
  964. */
  965. BOOL PUBLIC SADeleteAllItems(
  966. HSA psa)
  967. {
  968. ASSERT(IsSA(psa));
  969. if (psa->aItem)
  970. {
  971. MemFree(psa->hheap, psa->aItem);
  972. }
  973. psa->aItem = NULL;
  974. psa->cItem = psa->cItemAlloc = 0;
  975. return TRUE;
  976. }
  977. //================== Dynamic pointer array implementation ===========
  978. typedef struct _PA {
  979. // NOTE: The following two fields MUST be defined in this order, at
  980. // the beginning of the structure in order for the macro APIs to work.
  981. //
  982. DWORD cp;
  983. DWORD dwAlignPad;
  984. PVOID * pp;
  985. HANDLE hheap; // Heap to allocate from if NULL use shared
  986. DWORD cpAlloc;
  987. DWORD cpGrow;
  988. DWORD dwFlags;
  989. #ifdef DEBUG
  990. UINT magic;
  991. #endif
  992. } PA;
  993. /*----------------------------------------------------------
  994. Purpose: Creates a pointer array.
  995. Returns: TRUE
  996. FALSE (if out of memory)
  997. Cond: --
  998. */
  999. BOOL PUBLIC PACreateEx(
  1000. PHPA phpa,
  1001. DWORD cpGrow,
  1002. HANDLE hheap, // Must be non-null if PAF_HEAP set
  1003. DWORD dwFlags) // PAF_*
  1004. {
  1005. HPA ppa;
  1006. ASSERT(phpa);
  1007. ppa = PrvAlloc(dwFlags, hheap, sizeof(PA));
  1008. if (IsFlagSet(dwFlags, PAF_SHARED))
  1009. hheap = g_hSharedHeap;
  1010. if (ppa)
  1011. {
  1012. ppa->dwFlags = dwFlags;
  1013. ppa->cp = 0;
  1014. ppa->cpAlloc = 0;
  1015. ppa->cpGrow = (cpGrow < 8 ? 8 : cpGrow);
  1016. ppa->pp = NULL;
  1017. #ifdef WIN32
  1018. ppa->hheap = hheap;
  1019. #else
  1020. ppa->hheap = NULL;
  1021. #endif
  1022. #ifdef DEBUG
  1023. ppa->magic = PA_MAGIC;
  1024. #endif
  1025. }
  1026. *phpa = ppa;
  1027. return NULL != ppa;
  1028. }
  1029. /*----------------------------------------------------------
  1030. Purpose: Destroy a pointer array, and call the given pfnFree
  1031. function for each element in the array.
  1032. Returns: TRUE
  1033. FALSE (on failure)
  1034. Cond: --
  1035. */
  1036. BOOL PUBLIC PADestroyEx(
  1037. HPA ppa,
  1038. PFNPAFREE pfnFree,
  1039. LPARAM lParam)
  1040. {
  1041. ASSERT(IsPA(ppa));
  1042. if (ppa == NULL) // allow NULL for low memory cases, still assert
  1043. return TRUE;
  1044. if (ppa->pp)
  1045. {
  1046. if (pfnFree)
  1047. {
  1048. DWORD i = PAGetCount(ppa);
  1049. while (0 < i)
  1050. {
  1051. i--;
  1052. pfnFree(PAFastGetPtr(ppa, i), lParam);
  1053. }
  1054. }
  1055. if (!MemFree(ppa->hheap, ppa->pp))
  1056. return FALSE;
  1057. }
  1058. #ifdef DEBUG
  1059. ppa->cp = 0;
  1060. ppa->cpAlloc = 0;
  1061. ppa->magic = 0;
  1062. #endif
  1063. return MemFree(ppa->hheap, ppa);
  1064. }
  1065. /*----------------------------------------------------------
  1066. Purpose: Clone a pointer array. If *phpa was previously
  1067. allocated, this function simply grows the array
  1068. to the appropriate size before copying the contents
  1069. of the array.
  1070. Returns: TRUE
  1071. FALSE (if out of memory)
  1072. Cond: --
  1073. */
  1074. BOOL PUBLIC PAClone(
  1075. PHPA phpa,
  1076. HPA ppa)
  1077. {
  1078. BOOL bRet;
  1079. HPA ppaNew;
  1080. ASSERT(phpa);
  1081. if (NULL == *phpa)
  1082. {
  1083. bRet = PACreateEx(&ppaNew, ppa->cpGrow, ppa->hheap, ppa->dwFlags);
  1084. }
  1085. else
  1086. {
  1087. ppaNew = *phpa;
  1088. bRet = TRUE;
  1089. }
  1090. if (bRet)
  1091. {
  1092. bRet = PAGrow(ppaNew, ppa->cpAlloc);
  1093. if (!bRet)
  1094. {
  1095. if (NULL == *phpa)
  1096. PADestroy(ppaNew);
  1097. }
  1098. else
  1099. {
  1100. ppaNew->cp = ppa->cp;
  1101. hmemcpy(ppaNew->pp, ppa->pp, ppa->cp * sizeof(PVOID));
  1102. *phpa = ppaNew;
  1103. }
  1104. }
  1105. return bRet;
  1106. }
  1107. /*----------------------------------------------------------
  1108. Purpose: Get a pointer stored in index
  1109. Returns: TRUE
  1110. FALSE (if index out of range)
  1111. Cond: --
  1112. */
  1113. BOOL PUBLIC PAGetPtr(
  1114. HPA ppa,
  1115. DWORD index,
  1116. LPVOID * ppv)
  1117. {
  1118. BOOL bRet;
  1119. ASSERT(IsPA(ppa));
  1120. ASSERT(ppv);
  1121. bRet = !(PA_ERR == index || index >= ppa->cp);
  1122. if (bRet)
  1123. {
  1124. *ppv = ppa->pp[index];
  1125. }
  1126. else
  1127. {
  1128. *ppv = NULL;
  1129. }
  1130. return bRet;
  1131. }
  1132. /*----------------------------------------------------------
  1133. Purpose: Gets the index that pointer p is stored at
  1134. Returns: index
  1135. Cond: --
  1136. */
  1137. BOOL PUBLIC PAGetPtrIndex(
  1138. HPA ppa,
  1139. PVOID p,
  1140. LPDWORD pindex)
  1141. {
  1142. BOOL bRet = FALSE;
  1143. PVOID * pp;
  1144. PVOID * ppMax;
  1145. ASSERT(IsPA(ppa));
  1146. ASSERT(pindex);
  1147. if (ppa->pp)
  1148. {
  1149. pp = ppa->pp;
  1150. ppMax = pp + ppa->cp;
  1151. for ( ; pp < ppMax; pp++)
  1152. {
  1153. if (*pp == p)
  1154. {
  1155. *pindex = (DWORD)(pp - ppa->pp);
  1156. bRet = TRUE;
  1157. break;
  1158. }
  1159. }
  1160. }
  1161. if (!bRet)
  1162. *pindex = PA_ERR;
  1163. return bRet;
  1164. }
  1165. /*----------------------------------------------------------
  1166. Purpose: Grow the pointer array
  1167. Returns:
  1168. Cond: --
  1169. */
  1170. BOOL PUBLIC PAGrow(
  1171. HPA ppa,
  1172. DWORD cpAlloc)
  1173. {
  1174. ASSERT(IsPA(ppa));
  1175. if (cpAlloc > ppa->cpAlloc)
  1176. {
  1177. PVOID * ppNew;
  1178. cpAlloc = ((cpAlloc + ppa->cpGrow - 1) / ppa->cpGrow) * ppa->cpGrow;
  1179. if (ppa->pp)
  1180. ppNew = (PVOID *)MemReAlloc(ppa->hheap, ppa->pp, cpAlloc * sizeof(PVOID));
  1181. else
  1182. ppNew = (PVOID *)PrvAlloc(ppa->dwFlags, ppa->hheap, cpAlloc * sizeof(PVOID));
  1183. if (!ppNew)
  1184. return FALSE;
  1185. ppa->pp = ppNew;
  1186. ppa->cpAlloc = cpAlloc;
  1187. }
  1188. return TRUE;
  1189. }
  1190. /*----------------------------------------------------------
  1191. Purpose: Store a pointer at index. Grows the array accordingly.
  1192. Returns: TRUE
  1193. FALSE (if out of memory)
  1194. Cond: --
  1195. */
  1196. BOOL PUBLIC PASetPtr(
  1197. HPA ppa,
  1198. DWORD index,
  1199. PVOID p)
  1200. {
  1201. ASSERT(IsPA(ppa));
  1202. if (PA_ERR == index)
  1203. {
  1204. TRACE_MSG(TF_ERROR, "PA: Invalid index: %lu", index);
  1205. return FALSE;
  1206. }
  1207. if (index >= ppa->cp)
  1208. {
  1209. if (!PAGrow(ppa, index + 1))
  1210. return FALSE;
  1211. ppa->cp = index + 1;
  1212. }
  1213. ppa->pp[index] = p;
  1214. return TRUE;
  1215. }
  1216. /*----------------------------------------------------------
  1217. Purpose: Inserts the given item. If *piIndex is greater than
  1218. the current size of the array, the item is appended
  1219. to the end. Otherwise, the item is inserted at
  1220. *piIndex.
  1221. If piIndex is NULL, the item is appended to the end.
  1222. Use SASetItem to place an item at a specified index,
  1223. regardless of the array size.
  1224. When this function completes successfully, it sets
  1225. *piIndex to the index that the item really gets
  1226. inserted at. Otherwise, it sets *piIndex to SA_ERR.
  1227. Returns: TRUE (on successful insertion)
  1228. FALSE
  1229. Cond: --
  1230. */
  1231. BOOL PUBLIC PAInsertPtr(
  1232. HPA ppa,
  1233. LPDWORD pindex, // May be NULL
  1234. PVOID p)
  1235. {
  1236. BOOL bRet;
  1237. ASSERT(IsPA(ppa));
  1238. if (pindex && PA_ERR == *pindex)
  1239. {
  1240. TRACE_MSG(TF_ERROR, "PA: Invalid index: %lu", *pindex);
  1241. bRet = FALSE;
  1242. }
  1243. else
  1244. {
  1245. DWORD index;
  1246. bRet = TRUE; // assume success
  1247. if (NULL == pindex || *pindex > ppa->cp)
  1248. index = ppa->cp;
  1249. else
  1250. index = *pindex;
  1251. // Make sure we have room for one more item
  1252. //
  1253. if (ppa->cp + 1 > ppa->cpAlloc)
  1254. {
  1255. bRet = PAGrow(ppa, ppa->cp + 1);
  1256. }
  1257. if (bRet)
  1258. {
  1259. // If we are inserting, we need to slide everybody up
  1260. if (index < ppa->cp)
  1261. {
  1262. hmemcpy(&ppa->pp[index + 1], &ppa->pp[index],
  1263. (ppa->cp - index) * sizeof(PVOID));
  1264. }
  1265. ppa->pp[index] = p;
  1266. ppa->cp++;
  1267. if (pindex)
  1268. *pindex = index;
  1269. }
  1270. else if (pindex)
  1271. {
  1272. *pindex = PA_ERR;
  1273. }
  1274. }
  1275. return bRet;
  1276. }
  1277. /*----------------------------------------------------------
  1278. Purpose: Delete a pointer from index.
  1279. Returns: the deleted pointer
  1280. NULL (if index is out of range)
  1281. Cond: --
  1282. */
  1283. PVOID PUBLIC PADeletePtr(
  1284. HPA ppa,
  1285. DWORD index)
  1286. {
  1287. PVOID p;
  1288. ASSERT(IsPA(ppa));
  1289. if (PA_ERR == index || index >= ppa->cp)
  1290. {
  1291. TRACE_MSG(TF_ERROR, "PA: Invalid index: %lu", index);
  1292. return NULL;
  1293. }
  1294. p = ppa->pp[index];
  1295. if (index < ppa->cp - 1)
  1296. {
  1297. hmemcpy(&ppa->pp[index], &ppa->pp[index + 1],
  1298. (ppa->cp - (index + 1)) * sizeof(PVOID));
  1299. }
  1300. ppa->cp--;
  1301. if (ppa->cpAlloc - ppa->cp > ppa->cpGrow)
  1302. {
  1303. PVOID * ppNew;
  1304. ppNew = MemReAlloc(ppa->hheap, ppa->pp, (ppa->cpAlloc - ppa->cpGrow) * sizeof(PVOID));
  1305. ASSERT(ppNew);
  1306. ppa->pp = ppNew;
  1307. ppa->cpAlloc -= ppa->cpGrow;
  1308. }
  1309. return p;
  1310. }
  1311. /*----------------------------------------------------------
  1312. Purpose: Delete all the pointers in the array. If pfnFree
  1313. is non-NULL, this function will free each of the
  1314. pointer elements in this array using pfnFree.
  1315. Returns: TRUE
  1316. FALSE
  1317. Cond: --
  1318. */
  1319. BOOL PUBLIC PADeleteAllPtrsEx(
  1320. HPA ppa,
  1321. PFNPAFREE pfnFree,
  1322. LPARAM lParam)
  1323. {
  1324. ASSERT(IsPA(ppa));
  1325. if (ppa->pp)
  1326. {
  1327. if (pfnFree)
  1328. {
  1329. int i = PAGetCount(ppa);
  1330. while (0 < i)
  1331. {
  1332. i--;
  1333. pfnFree(PAFastGetPtr(ppa, i), lParam);
  1334. }
  1335. }
  1336. if (!MemFree(ppa->hheap, ppa->pp))
  1337. return FALSE;
  1338. }
  1339. ppa->pp = NULL;
  1340. ppa->cp = ppa->cpAlloc = 0;
  1341. return TRUE;
  1342. }
  1343. #ifdef USEQUICKSORT
  1344. BOOL NEAR PAQuickSort2(
  1345. DWORD i,
  1346. DWORD j,
  1347. SORTPARAMS FAR* psp)
  1348. {
  1349. PVOID * pp = psp->pp;
  1350. LPARAM lParam = psp->lParam;
  1351. PFNPACOMPARE pfnCmp = psp->pfnCmp;
  1352. DWORD iPivot;
  1353. PVOID pFirst;
  1354. DWORD k;
  1355. int result;
  1356. iPivot = PA_ERR;
  1357. pFirst = pp[i];
  1358. for (k = i + 1; k <= j; k++)
  1359. {
  1360. result = (*pfnCmp)(pp[k], pFirst, lParam);
  1361. if (result > 0)
  1362. {
  1363. iPivot = k;
  1364. break;
  1365. }
  1366. else if (result < 0)
  1367. {
  1368. iPivot = i;
  1369. break;
  1370. }
  1371. }
  1372. if (iPivot != PA_ERR)
  1373. {
  1374. DWORD l = i;
  1375. DWORD r = j;
  1376. PVOID pivot = pp[iPivot];
  1377. do
  1378. {
  1379. PVOID p;
  1380. p = pp[l];
  1381. pp[l] = pp[r];
  1382. pp[r] = p;
  1383. while ((*pfnCmp)(pp[l], pivot, lParam) < 0)
  1384. l++;
  1385. while ((*pfnCmp)(pp[r], pivot, lParam) >= 0)
  1386. r--;
  1387. }
  1388. while (l <= r);
  1389. if (l - 1 > i)
  1390. PAQuickSort2(i, l - 1, psp);
  1391. if (j > l)
  1392. PAQuickSort2(l, j, psp);
  1393. }
  1394. return TRUE;
  1395. }
  1396. BOOL NEAR PAQuickSort(
  1397. SORTPARAMS FAR* psp)
  1398. {
  1399. return PAQuickSort2(0, psp->cp - 1, psp);
  1400. }
  1401. #endif // USEQUICKSORT
  1402. #ifdef USEHEAPSORT
  1403. void NEAR PAHeapSortPushDown(
  1404. DWORD first,
  1405. DWORD last,
  1406. SORTPARAMS FAR* psp)
  1407. {
  1408. PVOID * pp = psp->pp;
  1409. LPARAM lParam = psp->lParam;
  1410. PFNPACOMPARE pfnCmp = psp->pfnCmp;
  1411. DWORD r;
  1412. DWORD r2;
  1413. r = first;
  1414. while (r <= last / 2)
  1415. {
  1416. int wRTo2R;
  1417. r2 = r * 2;
  1418. wRTo2R = (*pfnCmp)(pp[r-1], pp[r2-1], lParam);
  1419. if (r2 == last)
  1420. {
  1421. if (wRTo2R < 0)
  1422. {
  1423. Swap(pp[r-1], pp[r2-1]);
  1424. }
  1425. break;
  1426. }
  1427. else
  1428. {
  1429. int wR2toR21 = (*pfnCmp)(pp[r2-1], pp[r2+1-1], lParam);
  1430. if (wRTo2R < 0 && wR2toR21 >= 0)
  1431. {
  1432. Swap(pp[r-1], pp[r2-1]);
  1433. r = r2;
  1434. }
  1435. else if ((*pfnCmp)(pp[r-1], pp[r2+1-1], lParam) < 0 && wR2toR21 < 0)
  1436. {
  1437. Swap(pp[r-1], pp[r2+1-1]);
  1438. r = r2 + 1;
  1439. }
  1440. else
  1441. {
  1442. break;
  1443. }
  1444. }
  1445. }
  1446. }
  1447. BOOL NEAR PAHeapSort(SORTPARAMS FAR* psp)
  1448. {
  1449. PVOID * pp = psp->pp;
  1450. DWORD c = psp->cp;
  1451. DWORD i;
  1452. for (i = c / 2; i >= 1; i--)
  1453. PAHeapSortPushDown(i, c, psp);
  1454. for (i = c; i >= 2; i--)
  1455. {
  1456. Swap(pp[0], pp[i-1]);
  1457. PAHeapSortPushDown(1, i - 1, psp);
  1458. }
  1459. return TRUE;
  1460. }
  1461. #endif // USEHEAPSORT
  1462. #if defined(MERGESORT) && defined(WIN32)
  1463. #define SortCompare(psp, pp1, i1, pp2, i2) \
  1464. (psp->pfnCmp(pp1[i1], pp2[i2], psp->lParam))
  1465. //
  1466. // This function merges two sorted lists and makes one sorted list.
  1467. // psp->pp[iFirst, iFirst+cItes/2-1], psp->pp[iFirst+cItems/2, iFirst+cItems-1]
  1468. //
  1469. void NEAR PAMergeThem(
  1470. SORTPARAMS FAR* psp,
  1471. DWORD iFirst,
  1472. DWORD cItems)
  1473. {
  1474. //
  1475. // Notes:
  1476. // This function is separated from PAMergeSort2() to avoid comsuming
  1477. // stack variables. Never inline this.
  1478. //
  1479. DWORD cHalf = cItems/2;
  1480. DWORD iIn1, iIn2, iOut;
  1481. LPVOID * ppvSrc = &psp->pp[iFirst];
  1482. // Copy the first part to temp storage so we can write directly into
  1483. // the final buffer. Note that this takes at most psp->cp/2 DWORD's
  1484. hmemcpy(psp->ppT, ppvSrc, cHalf*sizeof(LPVOID));
  1485. for (iIn1=0, iIn2=cHalf, iOut=0;;)
  1486. {
  1487. if (SortCompare(psp, psp->ppT, iIn1, ppvSrc, iIn2) <= 0)
  1488. {
  1489. ppvSrc[iOut++] = psp->ppT[iIn1++];
  1490. if (iIn1==cHalf)
  1491. {
  1492. // We used up the first half; the rest of the second half
  1493. // should already be in place
  1494. break;
  1495. }
  1496. }
  1497. else
  1498. {
  1499. ppvSrc[iOut++] = ppvSrc[iIn2++];
  1500. if (iIn2==cItems)
  1501. {
  1502. // We used up the second half; copy the rest of the first half
  1503. // into place
  1504. hmemcpy(&ppvSrc[iOut], &psp->ppT[iIn1], (cItems-iOut)*sizeof(LPVOID));
  1505. break;
  1506. }
  1507. }
  1508. }
  1509. }
  1510. //
  1511. // This function sorts a give list (psp->pp[iFirst,iFirst-cItems-1]).
  1512. //
  1513. void NEAR PAMergeSort2(
  1514. SORTPARAMS FAR* psp,
  1515. DWORD iFirst,
  1516. DWORD cItems)
  1517. {
  1518. //
  1519. // Notes:
  1520. // This function is recursively called. Therefore, we should minimize
  1521. // the number of local variables and parameters. At this point, we
  1522. // use one local variable and three parameters.
  1523. //
  1524. DWORD cHalf;
  1525. switch(cItems)
  1526. {
  1527. case 1:
  1528. return;
  1529. case 2:
  1530. // Swap them, if they are out of order.
  1531. if (SortCompare(psp, psp->pp, iFirst, psp->pp, iFirst+1) > 0)
  1532. {
  1533. psp->ppT[0] = psp->pp[iFirst];
  1534. psp->pp[iFirst] = psp->pp[iFirst+1];
  1535. psp->pp[iFirst+1] = psp->ppT[0];
  1536. }
  1537. break;
  1538. default:
  1539. cHalf = cItems/2;
  1540. // Sort each half
  1541. PAMergeSort2(psp, iFirst, cHalf);
  1542. PAMergeSort2(psp, iFirst+cHalf, cItems-cHalf);
  1543. // Then, merge them.
  1544. PAMergeThem(psp, iFirst, cItems);
  1545. break;
  1546. }
  1547. }
  1548. BOOL NEAR PAMergeSort(
  1549. SORTPARAMS FAR* psp)
  1550. {
  1551. if (psp->cp == 0)
  1552. return TRUE;
  1553. // Note that we divide by 2 below; we want to round down
  1554. psp->ppT = GAlloc(psp->cp/2 * sizeof(LPVOID));
  1555. if (!psp->ppT)
  1556. return FALSE;
  1557. PAMergeSort2(psp, 0, psp->cp);
  1558. GFree(psp->ppT);
  1559. return TRUE;
  1560. }
  1561. #endif // MERGESORT
  1562. /*----------------------------------------------------------
  1563. Purpose: Sort the array.
  1564. Returns:
  1565. Cond: --
  1566. */
  1567. BOOL PUBLIC PASort(
  1568. HPA ppa,
  1569. PFNPACOMPARE pfnCmp,
  1570. LPARAM lParam)
  1571. {
  1572. SORTPARAMS sp;
  1573. sp.cp = ppa->cp;
  1574. sp.pp = ppa->pp;
  1575. sp.pfnCmp = pfnCmp;
  1576. sp.lParam = lParam;
  1577. #ifdef USEQUICKSORT
  1578. return PAQuickSort(&sp);
  1579. #endif
  1580. #ifdef USEHEAPSORT
  1581. return PAHeapSort(&sp);
  1582. #endif
  1583. #ifdef MERGESORT
  1584. return PAMergeSort(&sp);
  1585. #endif
  1586. }
  1587. /*----------------------------------------------------------
  1588. Purpose: Search for pFind in array.
  1589. Returns:
  1590. Cond: --
  1591. */
  1592. DWORD PUBLIC PASearch(
  1593. HPA ppa,
  1594. PVOID pFind,
  1595. DWORD iStart,
  1596. PFNPACOMPARE pfnCompare,
  1597. LPARAM lParam,
  1598. UINT options)
  1599. {
  1600. DWORD cp = PAGetCount(ppa);
  1601. ASSERT(pfnCompare);
  1602. ASSERT(PA_ERR != iStart);
  1603. // Only allow these wierd flags if the list is sorted
  1604. ASSERT((options & PAS_SORTED) || !(options & (PAS_INSERTBEFORE | PAS_INSERTAFTER)));
  1605. if (!(options & PAS_SORTED))
  1606. {
  1607. // Not sorted: do linear search.
  1608. DWORD i;
  1609. for (i = iStart; i < cp; i++)
  1610. {
  1611. if (0 == pfnCompare(pFind, PAFastGetPtr(ppa, i), lParam))
  1612. return i;
  1613. }
  1614. return PA_ERR;
  1615. }
  1616. else
  1617. {
  1618. // Search the array using binary search. If several adjacent
  1619. // elements match the target element, the index of the first
  1620. // matching element is returned.
  1621. DWORD iRet = PA_ERR; // assume no match
  1622. BOOL bFound = FALSE;
  1623. int nCmp = 0;
  1624. DWORD iLow = 0; // Don't bother using iStart for binary search
  1625. DWORD iMid = 0;
  1626. if (0 < cp)
  1627. {
  1628. DWORD iHigh = cp - 1;
  1629. // (OK for cp == 0)
  1630. while (iLow <= iHigh)
  1631. {
  1632. iMid = (iLow + iHigh) / 2;
  1633. nCmp = pfnCompare(pFind, PAFastGetPtr(ppa, iMid), lParam);
  1634. if (0 > nCmp)
  1635. {
  1636. // Account for the fact we are working with
  1637. // unsigned values
  1638. if (0 == iMid)
  1639. break;
  1640. iHigh = iMid - 1; // First is smaller
  1641. }
  1642. else if (0 < nCmp)
  1643. iLow = iMid + 1; // First is larger
  1644. else
  1645. {
  1646. // Match; search back for first match
  1647. bFound = TRUE;
  1648. while (0 < iMid)
  1649. {
  1650. if (0 != pfnCompare(pFind, PAFastGetPtr(ppa, iMid-1), lParam))
  1651. break;
  1652. else
  1653. iMid--;
  1654. }
  1655. break;
  1656. }
  1657. }
  1658. }
  1659. if (bFound)
  1660. {
  1661. ASSERT(0 <= iMid);
  1662. iRet = iMid;
  1663. }
  1664. // Did the search fail AND
  1665. // is one of the strange search flags set?
  1666. if (!bFound && (options & (PAS_INSERTAFTER | PAS_INSERTBEFORE)))
  1667. {
  1668. // Yes; return the index where the target should be inserted
  1669. // if not found
  1670. if (0 < nCmp) // First is larger
  1671. iRet = iLow;
  1672. else
  1673. iRet = iMid;
  1674. // (We don't distinguish between the two flags anymore)
  1675. }
  1676. else if ( !(options & (PAS_INSERTAFTER | PAS_INSERTBEFORE)) )
  1677. {
  1678. // Sanity check with linear search
  1679. ASSERT(PASearch(ppa, pFind, iStart, pfnCompare, lParam, options & ~PAS_SORTED) == iRet);
  1680. }
  1681. return iRet;
  1682. }
  1683. }
  1684. #endif // NODA
  1685. #endif // NOMEM