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

1639 lines
40 KiB

  1. #include "ctlspriv.h"
  2. //
  3. // Heapsort is a bit slower, but it doesn't use any stack or memory...
  4. // Mergesort takes a bit of memory (O(n)) and stack (O(log(n)), but very fast...
  5. //
  6. #define MERGESORT
  7. // #define USEHEAPSORT
  8. #ifdef DEBUG
  9. #define DSA_MAGIC ('S' | ('A' << 8))
  10. #define IsDSA(pdsa) ((pdsa) && (pdsa)->magic == DSA_MAGIC)
  11. #define DPA_MAGIC ('P' | ('A' << 8))
  12. #define IsDPA(pdpa) ((pdpa) && (pdpa)->magic == DPA_MAGIC)
  13. #else
  14. #define IsDSA(pdsa)
  15. #define IsDPA(pdsa)
  16. #endif
  17. typedef struct {
  18. void** pp;
  19. PFNDPACOMPARE pfnCmp;
  20. LPARAM lParam;
  21. int cp;
  22. #ifdef MERGESORT
  23. void** ppT;
  24. #endif
  25. } SORTPARAMS;
  26. BOOL DPA_QuickSort(SORTPARAMS* psp);
  27. BOOL DPA_QuickSort2(int i, int j, SORTPARAMS* psp);
  28. BOOL DPA_HeapSort(SORTPARAMS* psp);
  29. void DPA_HeapSortPushDown(int first, int last, SORTPARAMS* psp);
  30. BOOL DPA_MergeSort(SORTPARAMS* psp);
  31. void DPA_MergeSort2(SORTPARAMS* psp, int iFirst, int cItems);
  32. //========== Dynamic structure array ====================================
  33. // Dynamic structure array
  34. typedef struct _DSA
  35. {
  36. // NOTE: The following field MUST be defined at the beginning of the
  37. // structure in order for GetItemCount() to work.
  38. int cItem; // # of elements in dsa
  39. void* aItem; // memory for elements
  40. int cItemAlloc; // # items which fit in aItem
  41. int cbItem; // size of each item
  42. int cItemGrow; // # items to grow cItemAlloc by
  43. #ifdef DEBUG
  44. UINT magic;
  45. #endif
  46. } DSA;
  47. #define DSA_PITEM(pdsa, index) ((void*)(((BYTE*)(pdsa)->aItem) + ((index) * (pdsa)->cbItem)))
  48. #ifdef DEBUG
  49. #define BF_ONDAVALIDATE 0x00001000
  50. void DABreakFn(void)
  51. {
  52. if (IsFlagSet(g_dwBreakFlags, BF_ONDAVALIDATE))
  53. ASSERT(0);
  54. }
  55. #define DABreak() DABreakFn()
  56. #else
  57. #define DABreak()
  58. #endif
  59. HDSA WINAPI DSA_Create(int cbItem, int cItemGrow)
  60. {
  61. HDSA pdsa = Alloc(sizeof(DSA));
  62. ASSERT(cbItem);
  63. if (pdsa)
  64. {
  65. ASSERT(pdsa->cItem == 0);
  66. ASSERT(pdsa->cItemAlloc == 0);
  67. pdsa->cbItem = cbItem;
  68. pdsa->cItemGrow = (cItemGrow == 0 ? 1 : cItemGrow);
  69. ASSERT(pdsa->aItem == NULL);
  70. #ifdef DEBUG
  71. pdsa->magic = DSA_MAGIC;
  72. #endif
  73. }
  74. return pdsa;
  75. }
  76. BOOL WINAPI DSA_Destroy(HDSA pdsa)
  77. {
  78. if (pdsa == NULL)
  79. {
  80. // allow NULL for low memory cases
  81. return TRUE;
  82. }
  83. // Components rely on not having to check for NULL
  84. ASSERT(IsDSA(pdsa));
  85. #ifdef DEBUG
  86. pdsa->cItem = 0;
  87. pdsa->cItemAlloc = 0;
  88. pdsa->cbItem = 0;
  89. pdsa->magic = 0;
  90. #endif
  91. if (pdsa->aItem && !Free(pdsa->aItem))
  92. {
  93. return FALSE;
  94. }
  95. return Free(pdsa);
  96. }
  97. void WINAPI DSA_EnumCallback(HDSA pdsa, PFNDSAENUMCALLBACK pfnCB, void *pData)
  98. {
  99. int i;
  100. if (!pdsa)
  101. return;
  102. ASSERT(IsDSA(pdsa));
  103. for (i = 0; i < pdsa->cItem; i++) {
  104. if (!pfnCB(DSA_GetItemPtr(pdsa, i), pData))
  105. break;
  106. }
  107. }
  108. void WINAPI DSA_DestroyCallback(HDSA pdsa, PFNDSAENUMCALLBACK pfnCB, void *pData)
  109. {
  110. DSA_EnumCallback(pdsa, pfnCB, pData);
  111. DSA_Destroy(pdsa);
  112. }
  113. BOOL WINAPI DSA_GetItem(HDSA pdsa, int index, void* pitem)
  114. {
  115. ASSERT(IsDSA(pdsa));
  116. ASSERT(pitem);
  117. if (index < 0 || index >= pdsa->cItem)
  118. {
  119. #ifdef DEBUG
  120. // Don't assert if index == pdsa->cItems as some clients simply want to walk the list and no need to call getcount...
  121. if (index != pdsa->cItem)
  122. {
  123. DebugMsg(DM_ERROR, TEXT("DSA: GetItem: Invalid index: %d"), index);
  124. DABreak();
  125. }
  126. #endif
  127. return FALSE;
  128. }
  129. CopyMemory(pitem, DSA_PITEM(pdsa, index), pdsa->cbItem);
  130. return TRUE;
  131. }
  132. void* WINAPI DSA_GetItemPtr(HDSA pdsa, int index)
  133. {
  134. ASSERT(IsDSA(pdsa));
  135. if (index < 0 || index >= pdsa->cItem)
  136. {
  137. #ifdef DEBUG
  138. // Don't assert if index == pdsa->cItems as some clients simply want to walk the list and no need to call getcount...
  139. if (index != pdsa->cItem)
  140. {
  141. DebugMsg(DM_ERROR, TEXT("DSA: GetItemPtr: Invalid index: %d"), index);
  142. // DABreak(); // caller knows
  143. }
  144. #endif
  145. return NULL;
  146. }
  147. return DSA_PITEM(pdsa, index);
  148. }
  149. BOOL DSA_ForceGrow(HDSA pdsa, int iNumberToAdd)
  150. {
  151. ASSERT(IsDSA(pdsa));
  152. if (!pdsa)
  153. return FALSE;
  154. if (pdsa->cItem + iNumberToAdd > pdsa->cItemAlloc)
  155. {
  156. int cItemAlloc = (((pdsa->cItemAlloc + iNumberToAdd) + pdsa->cItemGrow - 1) / pdsa->cItemGrow) * pdsa->cItemGrow;
  157. void* aItemNew = ReAlloc(pdsa->aItem, cItemAlloc * pdsa->cbItem);
  158. if (!aItemNew)
  159. {
  160. return FALSE;
  161. }
  162. pdsa->aItem = aItemNew;
  163. pdsa->cItemAlloc = cItemAlloc;
  164. }
  165. return TRUE;
  166. }
  167. BOOL WINAPI DSA_SetItem(HDSA pdsa, int index, void* pitem)
  168. {
  169. ASSERT(pitem);
  170. ASSERT(IsDSA(pdsa));
  171. if (index < 0)
  172. {
  173. DebugMsg(DM_ERROR, TEXT("DSA: SetItem: Invalid index: %d"), index);
  174. DABreak();
  175. return FALSE;
  176. }
  177. if (index >= pdsa->cItem)
  178. {
  179. if (index + 1 > pdsa->cItemAlloc)
  180. {
  181. int cItemAlloc = (((index + 1) + pdsa->cItemGrow - 1) / pdsa->cItemGrow) * pdsa->cItemGrow;
  182. void* aItemNew = ReAlloc(pdsa->aItem, cItemAlloc * pdsa->cbItem);
  183. if (!aItemNew)
  184. {
  185. return FALSE;
  186. }
  187. pdsa->aItem = aItemNew;
  188. pdsa->cItemAlloc = cItemAlloc;
  189. }
  190. pdsa->cItem = index + 1;
  191. }
  192. CopyMemory(DSA_PITEM(pdsa, index), pitem, pdsa->cbItem);
  193. return TRUE;
  194. }
  195. int WINAPI DSA_InsertItem(HDSA pdsa, int index, void* pitem)
  196. {
  197. ASSERT(pitem);
  198. ASSERT(IsDSA(pdsa));
  199. if (index < 0)
  200. {
  201. DebugMsg(DM_ERROR, TEXT("DSA: InsertItem: Invalid index: %d"), index);
  202. DABreak();
  203. return -1;
  204. }
  205. if (index > pdsa->cItem)
  206. index = pdsa->cItem;
  207. if (pdsa->cItem + 1 > pdsa->cItemAlloc)
  208. {
  209. void* aItemNew = ReAlloc(pdsa->aItem, (pdsa->cItemAlloc + pdsa->cItemGrow) * pdsa->cbItem);
  210. if (!aItemNew)
  211. {
  212. return -1;
  213. }
  214. pdsa->aItem = aItemNew;
  215. pdsa->cItemAlloc += pdsa->cItemGrow;
  216. }
  217. if (index < pdsa->cItem)
  218. {
  219. MoveMemory(DSA_PITEM(pdsa, index + 1),
  220. DSA_PITEM(pdsa, index),
  221. (pdsa->cItem - index) * pdsa->cbItem);
  222. }
  223. pdsa->cItem++;
  224. MoveMemory(DSA_PITEM(pdsa, index), pitem, pdsa->cbItem);
  225. return index;
  226. }
  227. BOOL WINAPI DSA_DeleteItem(HDSA pdsa, int index)
  228. {
  229. ASSERT(IsDSA(pdsa));
  230. if (index < 0 || index >= pdsa->cItem)
  231. {
  232. DebugMsg(DM_ERROR, TEXT("DSA: DeleteItem: Invalid index: %d"), index);
  233. DABreak();
  234. return FALSE;
  235. }
  236. if (index < pdsa->cItem - 1)
  237. {
  238. MoveMemory(DSA_PITEM(pdsa, index),
  239. DSA_PITEM(pdsa, index + 1),
  240. (pdsa->cItem - (index + 1)) * pdsa->cbItem);
  241. }
  242. pdsa->cItem--;
  243. if (pdsa->cItemAlloc - pdsa->cItem > pdsa->cItemGrow)
  244. {
  245. void* aItemNew = ReAlloc(pdsa->aItem, (pdsa->cItemAlloc - pdsa->cItemGrow) * pdsa->cbItem);
  246. if (aItemNew)
  247. {
  248. pdsa->aItem = aItemNew;
  249. }
  250. else
  251. {
  252. // If the shrink fails, then just continue with the old (slightly
  253. // too big) allocation. Go ahead and let cItemAlloc decrease
  254. // so we don't keep trying to realloc smaller
  255. }
  256. pdsa->cItemAlloc -= pdsa->cItemGrow;
  257. }
  258. return TRUE;
  259. }
  260. BOOL WINAPI DSA_DeleteAllItems(HDSA pdsa)
  261. {
  262. ASSERT(IsDSA(pdsa));
  263. if (pdsa->aItem && !Free(pdsa->aItem))
  264. {
  265. return FALSE;
  266. }
  267. pdsa->aItem = NULL;
  268. pdsa->cItem = pdsa->cItemAlloc = 0;
  269. return TRUE;
  270. }
  271. //================== Dynamic pointer array implementation ===========
  272. typedef struct _DPA
  273. {
  274. // NOTE: The following two fields MUST be defined in this order, at
  275. // the beginning of the structure in order for the macro APIs to work.
  276. int cp;
  277. void** pp;
  278. HANDLE hheap; // Heap to allocate from if NULL use shared
  279. int cpAlloc;
  280. int cpGrow;
  281. #ifdef DEBUG
  282. UINT magic;
  283. #endif
  284. } DPA;
  285. HDPA WINAPI DPA_Create(int cpGrow)
  286. {
  287. return DPA_CreateEx(cpGrow, NULL);
  288. }
  289. // Should nuke the standard DPA above...
  290. HDPA WINAPI DPA_CreateEx(int cpGrow, HANDLE hheap)
  291. {
  292. HDPA pdpa;
  293. if (hheap == NULL)
  294. {
  295. hheap = GetProcessHeap();
  296. pdpa = ALLOC_NULLHEAP(hheap, sizeof(DPA));
  297. }
  298. else
  299. pdpa = ControlAlloc(hheap, sizeof(DPA));
  300. if (pdpa)
  301. {
  302. ASSERT(pdpa->cp == 0);
  303. ASSERT(pdpa->cpAlloc == 0);
  304. pdpa->cpGrow = (cpGrow < 8 ? 8 : cpGrow);
  305. ASSERT(pdpa->pp == NULL);
  306. pdpa->hheap = hheap;
  307. #ifdef DEBUG
  308. pdpa->magic = DPA_MAGIC;
  309. #endif
  310. }
  311. return pdpa;
  312. }
  313. BOOL WINAPI DPA_Destroy(HDPA pdpa)
  314. {
  315. if (pdpa == NULL) // allow NULL for low memory cases, still assert
  316. return TRUE;
  317. ASSERT(IsDPA(pdpa));
  318. #ifndef UNIX
  319. ASSERT(pdpa->hheap);
  320. #endif
  321. #ifdef DEBUG
  322. pdpa->cp = 0;
  323. pdpa->cpAlloc = 0;
  324. pdpa->magic = 0;
  325. #endif
  326. if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp))
  327. return FALSE;
  328. return ControlFree(pdpa->hheap, pdpa);
  329. }
  330. HDPA WINAPI DPA_Clone(HDPA pdpa, HDPA pdpaNew)
  331. {
  332. BOOL fAlloc = FALSE;
  333. if (!pdpaNew)
  334. {
  335. pdpaNew = DPA_CreateEx(pdpa->cpGrow, pdpa->hheap);
  336. if (!pdpaNew)
  337. {
  338. return NULL;
  339. }
  340. fAlloc = TRUE;
  341. }
  342. if (!DPA_Grow(pdpaNew, pdpa->cpAlloc))
  343. {
  344. if (!fAlloc)
  345. {
  346. DPA_Destroy(pdpaNew);
  347. }
  348. return NULL;
  349. }
  350. pdpaNew->cp = pdpa->cp;
  351. CopyMemory(pdpaNew->pp, pdpa->pp, pdpa->cp * sizeof(void*));
  352. return pdpaNew;
  353. }
  354. void* WINAPI DPA_GetPtr(HDPA pdpa, INT_PTR index)
  355. {
  356. ASSERT(IsDPA(pdpa));
  357. if (!pdpa || index < 0 || index >= pdpa->cp)
  358. return NULL;
  359. return pdpa->pp[index];
  360. }
  361. int WINAPI DPA_GetPtrIndex(HDPA pdpa, void* p)
  362. {
  363. void** pp;
  364. void** ppMax;
  365. ASSERT(IsDPA(pdpa));
  366. if (pdpa && pdpa->pp)
  367. {
  368. pp = pdpa->pp;
  369. ppMax = pp + pdpa->cp;
  370. for ( ; pp < ppMax; pp++)
  371. {
  372. if (*pp == p)
  373. return (int) (pp - pdpa->pp);
  374. }
  375. }
  376. return -1;
  377. }
  378. BOOL WINAPI DPA_Grow(HDPA pdpa, int cpAlloc)
  379. {
  380. ASSERT(IsDPA(pdpa));
  381. if (!pdpa)
  382. return FALSE;
  383. if (cpAlloc > pdpa->cpAlloc)
  384. {
  385. void** ppNew;
  386. cpAlloc = ((cpAlloc + pdpa->cpGrow - 1) / pdpa->cpGrow) * pdpa->cpGrow;
  387. if (pdpa->pp)
  388. ppNew = (void**)ControlReAlloc(pdpa->hheap, pdpa->pp, cpAlloc * sizeof(void*));
  389. else
  390. ppNew = (void**)ControlAlloc(pdpa->hheap, cpAlloc * sizeof(void*));
  391. if (!ppNew)
  392. return FALSE;
  393. pdpa->pp = ppNew;
  394. pdpa->cpAlloc = cpAlloc;
  395. //
  396. // Grow more agressively as we get bigger, up to a maximum of
  397. // 512 at a time. Note, we'll only hit our outer bound growth
  398. // at a time limit once we've already got that many items in the
  399. // DPA anyway...
  400. //
  401. if (pdpa->cpGrow < 256)
  402. {
  403. pdpa->cpGrow = pdpa->cpGrow << 1;
  404. }
  405. }
  406. return TRUE;
  407. }
  408. BOOL WINAPI DPA_SetPtr(HDPA pdpa, int index, void* p)
  409. {
  410. ASSERT(IsDPA(pdpa));
  411. if (!pdpa)
  412. return FALSE;
  413. if (index < 0)
  414. {
  415. DebugMsg(DM_ERROR, TEXT("DPA: SetPtr: Invalid index: %d"), index);
  416. DABreak();
  417. return FALSE;
  418. }
  419. if (index >= pdpa->cp)
  420. {
  421. if (!DPA_Grow(pdpa, index + 1))
  422. return FALSE;
  423. // If we grew by more than one, must zero-init all the stuff in the middle
  424. ZeroMemory(pdpa->pp + pdpa->cp, sizeof(void *) * (index - pdpa->cp));
  425. pdpa->cp = index + 1;
  426. }
  427. pdpa->pp[index] = p;
  428. return TRUE;
  429. }
  430. int WINAPI DPA_InsertPtr(HDPA pdpa, int index, void* p)
  431. {
  432. ASSERT(IsDPA(pdpa));
  433. if (!pdpa)
  434. return -1;
  435. if (index < 0)
  436. {
  437. DebugMsg(DM_ERROR, TEXT("DPA: InsertPtr: Invalid index: %d"), index);
  438. DABreak();
  439. return -1;
  440. }
  441. if (index > pdpa->cp)
  442. index = pdpa->cp;
  443. // Make sure we have room for one more item
  444. //
  445. if (pdpa->cp + 1 > pdpa->cpAlloc)
  446. {
  447. if (!DPA_Grow(pdpa, pdpa->cp + 1))
  448. return -1;
  449. }
  450. // If we are inserting, we need to slide everybody up
  451. //
  452. if (index < pdpa->cp)
  453. {
  454. MoveMemory(&pdpa->pp[index + 1],
  455. &pdpa->pp[index],
  456. (pdpa->cp - index) * sizeof(void*));
  457. }
  458. pdpa->pp[index] = p;
  459. pdpa->cp++;
  460. return index;
  461. }
  462. void* WINAPI DPA_DeletePtr(HDPA pdpa, int index)
  463. {
  464. void* p;
  465. ASSERT(IsDPA(pdpa));
  466. if (!pdpa)
  467. return FALSE;
  468. if (index < 0 || index >= pdpa->cp)
  469. {
  470. DebugMsg(DM_ERROR, TEXT("DPA: DeltePtr: Invalid index: %d"), index);
  471. DABreak();
  472. return NULL;
  473. }
  474. p = pdpa->pp[index];
  475. if (index < pdpa->cp - 1)
  476. {
  477. MoveMemory(&pdpa->pp[index],
  478. &pdpa->pp[index + 1],
  479. (pdpa->cp - (index + 1)) * sizeof(void*));
  480. }
  481. pdpa->cp--;
  482. if (pdpa->cpAlloc - pdpa->cp > pdpa->cpGrow)
  483. {
  484. void** ppNew;
  485. ppNew = ControlReAlloc(pdpa->hheap, pdpa->pp, (pdpa->cpAlloc - pdpa->cpGrow) * sizeof(void*));
  486. if (ppNew)
  487. pdpa->pp = ppNew;
  488. else
  489. {
  490. // If the shrink fails, then just continue with the old (slightly
  491. // too big) allocation. Go ahead and let cpAlloc decrease
  492. // so we don't keep trying to realloc smaller
  493. }
  494. pdpa->cpAlloc -= pdpa->cpGrow;
  495. }
  496. return p;
  497. }
  498. BOOL WINAPI DPA_DeleteAllPtrs(HDPA pdpa)
  499. {
  500. if (!pdpa)
  501. return FALSE;
  502. ASSERT(IsDPA(pdpa));
  503. if (pdpa->pp && !ControlFree(pdpa->hheap, pdpa->pp))
  504. return FALSE;
  505. pdpa->pp = NULL;
  506. pdpa->cp = pdpa->cpAlloc = 0;
  507. return TRUE;
  508. }
  509. void WINAPI DPA_EnumCallback(HDPA pdpa, PFNDPAENUMCALLBACK pfnCB, void *pData)
  510. {
  511. int i;
  512. if (!pdpa)
  513. return;
  514. ASSERT(IsDPA(pdpa));
  515. for (i = 0; i < pdpa->cp; i++) {
  516. if (!pfnCB(DPA_FastGetPtr(pdpa, i), pData))
  517. break;
  518. }
  519. }
  520. void WINAPI DPA_DestroyCallback(HDPA pdpa, PFNDPAENUMCALLBACK pfnCB, void *pData)
  521. {
  522. DPA_EnumCallback(pdpa, pfnCB, pData);
  523. DPA_Destroy(pdpa);
  524. }
  525. typedef struct _DPASTREAMHEADER
  526. {
  527. DWORD cbSize; // Size of entire stream
  528. DWORD dwVersion; // For versioning
  529. int celem;
  530. } DPASTREAMHEADER;
  531. #define DPASTREAM_VERSION 1
  532. /*----------------------------------------------------------
  533. Purpose: Saves the DPA to a stream by writing out a header,
  534. and then calling the given callback to write each
  535. element.
  536. The callback can end the write early by returning
  537. something other than S_OK. Returning an error will
  538. cancel the entire write. Returning S_FALSE will
  539. stop the write.
  540. Returns: S_OK or S_FALSE for success.
  541. S_FALSE only if callback stops early
  542. errors
  543. */
  544. HRESULT
  545. WINAPI
  546. DPA_SaveStream(
  547. IN HDPA pdpa,
  548. IN PFNDPASTREAM pfn,
  549. IN IStream * pstm,
  550. IN void * pvInstData)
  551. {
  552. HRESULT hres = E_INVALIDARG;
  553. if (IS_VALID_HANDLE(pdpa, DPA) &&
  554. IS_VALID_CODE_PTR(pstm, IStream *) &&
  555. IS_VALID_CODE_PTR(pfn, PFNDPASTREAM))
  556. {
  557. DPASTREAMHEADER header;
  558. LARGE_INTEGER dlibMove = { 0 };
  559. ULARGE_INTEGER ulPosBegin;
  560. // Get the current seek position, so we can update the header
  561. // once we know how much we've written
  562. hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &ulPosBegin);
  563. if (SUCCEEDED(hres))
  564. {
  565. // Write the header (we will update some of this once we're
  566. // finished)
  567. header.cbSize = 0;
  568. header.dwVersion = DPASTREAM_VERSION;
  569. header.celem = 0;
  570. // First write out the header
  571. hres = pstm->lpVtbl->Write(pstm, &header, sizeof(header), NULL);
  572. if (SUCCEEDED(hres))
  573. {
  574. DPASTREAMINFO info;
  575. int cel = DPA_GetPtrCount(pdpa);
  576. void **ppv = DPA_GetPtrPtr(pdpa);
  577. // This keeps the count of what is actually written
  578. info.iPos = 0;
  579. // Write each element
  580. for (; 0 < cel; cel--, ppv++)
  581. {
  582. info.pvItem = *ppv;
  583. hres = pfn(&info, pstm, pvInstData);
  584. // Returning S_FALSE from callback means it didn't
  585. // write anything for this element, so don't increment
  586. // the iPos (which refers to the count written).
  587. if (S_OK == hres)
  588. info.iPos++;
  589. else if (FAILED(hres))
  590. {
  591. hres = S_FALSE;
  592. break;
  593. }
  594. }
  595. if (FAILED(hres))
  596. {
  597. // Reposition pointer to beginning
  598. dlibMove.LowPart = ulPosBegin.LowPart;
  599. dlibMove.HighPart = ulPosBegin.HighPart;
  600. pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
  601. }
  602. else
  603. {
  604. ULARGE_INTEGER ulPosEnd;
  605. // Calculate how much was written
  606. hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR,
  607. &ulPosEnd);
  608. if (SUCCEEDED(hres))
  609. {
  610. // We only save the low part
  611. ASSERT(ulPosEnd.HighPart == ulPosBegin.HighPart);
  612. // Update the header
  613. header.celem = info.iPos;
  614. header.cbSize = ulPosEnd.LowPart - ulPosBegin.LowPart;
  615. dlibMove.LowPart = ulPosBegin.LowPart;
  616. dlibMove.HighPart = ulPosBegin.HighPart;
  617. pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
  618. pstm->lpVtbl->Write(pstm, &header, sizeof(header), NULL);
  619. // Reposition pointer
  620. dlibMove.LowPart = ulPosEnd.LowPart;
  621. dlibMove.HighPart = ulPosEnd.HighPart;
  622. pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
  623. }
  624. }
  625. }
  626. }
  627. }
  628. return hres;
  629. }
  630. /*----------------------------------------------------------
  631. Purpose: Loads the DPA from a stream by calling the given callback
  632. to read each element.
  633. The callback can end the read early by returning
  634. something other than S_OK.
  635. Returns: S_OK on success
  636. S_FALSE if the callback aborted early or the stream ended
  637. abruptly. DPA is partially filled.
  638. error on anything else
  639. */
  640. HRESULT
  641. WINAPI
  642. DPA_LoadStream(
  643. OUT HDPA * ppdpa,
  644. IN PFNDPASTREAM pfn,
  645. IN IStream * pstm,
  646. IN void * pvInstData)
  647. {
  648. HRESULT hres = E_INVALIDARG;
  649. if (IS_VALID_WRITE_PTR(ppdpa, HDPA) &&
  650. IS_VALID_CODE_PTR(pstm, IStream *) &&
  651. IS_VALID_CODE_PTR(pfn, PFNDPASTREAM))
  652. {
  653. DPASTREAMHEADER header;
  654. LARGE_INTEGER dlibMove = { 0 };
  655. ULARGE_INTEGER ulPosBegin;
  656. ULONG cbRead;
  657. *ppdpa = NULL;
  658. // Get the current seek position so we can position pointer
  659. // correctly upon error.
  660. hres = pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_CUR, &ulPosBegin);
  661. if (SUCCEEDED(hres))
  662. {
  663. // Read the header
  664. hres = pstm->lpVtbl->Read(pstm, &header, sizeof(header), &cbRead);
  665. if (SUCCEEDED(hres))
  666. {
  667. if (sizeof(header) > cbRead ||
  668. sizeof(header) > header.cbSize ||
  669. DPASTREAM_VERSION != header.dwVersion)
  670. {
  671. hres = E_FAIL;
  672. }
  673. else
  674. {
  675. // Create the list
  676. HDPA pdpa = DPA_Create(header.celem);
  677. if ( !pdpa || !DPA_Grow(pdpa, header.celem))
  678. hres = E_OUTOFMEMORY;
  679. else
  680. {
  681. // Read each element
  682. DPASTREAMINFO info;
  683. void **ppv = DPA_GetPtrPtr(pdpa);
  684. for (info.iPos = 0; info.iPos < header.celem; )
  685. {
  686. info.pvItem = NULL;
  687. hres = pfn(&info, pstm, pvInstData);
  688. // Returning S_FALSE from the callback means
  689. // it skipped this stream element.
  690. // Don't increment iPos (which refers to the
  691. // count read).
  692. if (S_OK == hres)
  693. {
  694. *ppv = info.pvItem;
  695. info.iPos++;
  696. ppv++;
  697. }
  698. else if (FAILED(hres))
  699. {
  700. hres = S_FALSE;
  701. break;
  702. }
  703. }
  704. pdpa->cp = info.iPos;
  705. *ppdpa = pdpa;
  706. }
  707. }
  708. // Reposition pointer if we failed
  709. if (S_OK != hres)
  710. {
  711. if (S_FALSE == hres)
  712. {
  713. // Position pointer to the end
  714. dlibMove.LowPart = ulPosBegin.LowPart + header.cbSize;
  715. }
  716. else
  717. {
  718. // Position pointer to beginning
  719. dlibMove.LowPart = ulPosBegin.LowPart;
  720. }
  721. dlibMove.HighPart = ulPosBegin.HighPart;
  722. pstm->lpVtbl->Seek(pstm, dlibMove, STREAM_SEEK_SET, NULL);
  723. }
  724. }
  725. }
  726. ASSERT(SUCCEEDED(hres) && *ppdpa ||
  727. FAILED(hres) && NULL == *ppdpa);
  728. }
  729. return hres;
  730. }
  731. /*----------------------------------------------------------
  732. Purpose: Merge two DPAs. This takes two arrays and merges the
  733. source array into the destination.
  734. Merge options:
  735. DPAM_SORTED The arrays are already sorted; don't sort
  736. DPAM_UNION The resulting array is the union of all elements
  737. in both arrays.
  738. DPAM_INTERSECT Only elements in the source array that intersect
  739. with the dest array are merged.
  740. DPAM_NORMAL Like DPAM_INTERSECT except the dest array
  741. also maintains its original, additional elements.
  742. Returns: S_OK for success.
  743. errors if merge fails
  744. Cond: --
  745. */
  746. BOOL
  747. WINAPI
  748. DPA_Merge(
  749. IN HDPA pdpaDest,
  750. IN HDPA pdpaSrc,
  751. IN DWORD dwFlags,
  752. IN PFNDPACOMPARE pfnCompare,
  753. IN PFNDPAMERGE pfnMerge,
  754. IN LPARAM lParam)
  755. {
  756. BOOL bRet = FALSE;
  757. if (IS_VALID_HANDLE(pdpaSrc, DPA) &&
  758. IS_VALID_HANDLE(pdpaDest, DPA) &&
  759. IS_VALID_CODE_PTR(pfnCompare, PFNDPACOMPARE) &&
  760. IS_VALID_CODE_PTR(pfnMerge, PFNDPAMERGE))
  761. {
  762. int iSrc;
  763. int iDest;
  764. int nCmp;
  765. void **ppvSrc;
  766. void **ppvDest;
  767. bRet = TRUE;
  768. // Are the arrays already sorted?
  769. if ( !(dwFlags & DPAM_SORTED) )
  770. {
  771. // No; sort them
  772. DPA_Sort(pdpaSrc, pfnCompare, lParam);
  773. DPA_Sort(pdpaDest, pfnCompare, lParam);
  774. }
  775. // This merges in-place. The size of the resulting DPA
  776. // depends on the options:
  777. //
  778. // DPAM_NORMAL Same size as the dest DPA before
  779. // the merge.
  780. //
  781. // DPAM_UNION Min size is the larger of the two.
  782. // Max size is the sum of the two.
  783. //
  784. // DPAM_INTERSECT Min size is zero.
  785. // Max size is the smaller of the two.
  786. //
  787. // We iterate backwards to minimize the amount of moves we
  788. // incur by calling DPA_DeletePtr.
  789. //
  790. iSrc = pdpaSrc->cp - 1;
  791. iDest = pdpaDest->cp - 1;
  792. ppvSrc = &DPA_FastGetPtr(pdpaSrc, iSrc);
  793. ppvDest = &DPA_FastGetPtr(pdpaDest, iDest);
  794. while (0 <= iSrc && 0 <= iDest)
  795. {
  796. void *pv;
  797. nCmp = pfnCompare(*ppvDest, *ppvSrc, lParam);
  798. if (0 == nCmp)
  799. {
  800. // Elements match; merge them.
  801. pv = pfnMerge(DPAMM_MERGE, *ppvDest, *ppvSrc, lParam);
  802. if (NULL == pv)
  803. {
  804. bRet = FALSE;
  805. break;
  806. }
  807. *ppvDest = pv;
  808. iSrc--;
  809. ppvSrc--;
  810. iDest--;
  811. ppvDest--;
  812. }
  813. else if (0 < nCmp)
  814. {
  815. // pvSrc < pvDest. The source array doesn't have pvDest.
  816. if (dwFlags & DPAM_INTERSECT)
  817. {
  818. // Delete pvDest
  819. pfnMerge(DPAMM_DELETE, DPA_DeletePtr(pdpaDest, iDest), NULL, lParam);
  820. }
  821. else
  822. {
  823. ; // Keep it (do nothing)
  824. }
  825. // Move onto the next element in the dest array
  826. iDest--;
  827. ppvDest--;
  828. }
  829. else
  830. {
  831. // pvSrc > pvDest. The dest array doesn't have pvSrc.
  832. if (dwFlags & DPAM_UNION)
  833. {
  834. // Add pvSrc
  835. pv = pfnMerge(DPAMM_INSERT, *ppvSrc, NULL, lParam);
  836. if (NULL == pv)
  837. {
  838. bRet = FALSE;
  839. break;
  840. }
  841. DPA_InsertPtr(pdpaDest, iDest+1, pv);
  842. // DPA_InsertPtr may end up reallocating the pointer array
  843. // thus making ppvDest invalid
  844. ppvDest = &DPA_FastGetPtr(pdpaDest, iDest);
  845. }
  846. else
  847. {
  848. ; // Skip it (do nothing)
  849. }
  850. // Move onto the next element in the source array
  851. iSrc--;
  852. ppvSrc--;
  853. }
  854. }
  855. // there are some items left in src
  856. if ((dwFlags & DPAM_UNION) && 0 <= iSrc)
  857. {
  858. for (; 0 <= iSrc; iSrc--, ppvSrc--)
  859. {
  860. void *pv = pfnMerge(DPAMM_INSERT, *ppvSrc, NULL, lParam);
  861. if (NULL == pv)
  862. {
  863. bRet = FALSE;
  864. break;
  865. }
  866. DPA_InsertPtr(pdpaDest, 0, pv);
  867. }
  868. }
  869. }
  870. return bRet;
  871. }
  872. BOOL WINAPI DPA_Sort(HDPA pdpa, PFNDPACOMPARE pfnCmp, LPARAM lParam)
  873. {
  874. SORTPARAMS sp;
  875. sp.cp = pdpa->cp;
  876. sp.pp = pdpa->pp;
  877. sp.pfnCmp = pfnCmp;
  878. sp.lParam = lParam;
  879. #ifdef USEQUICKSORT
  880. return DPA_QuickSort(&sp);
  881. #endif
  882. #ifdef USEHEAPSORT
  883. return DPA_HeapSort(&sp);
  884. #endif
  885. #ifdef MERGESORT
  886. return DPA_MergeSort(&sp);
  887. #endif
  888. }
  889. #ifdef USEQUICKSORT
  890. BOOL DPA_QuickSort(SORTPARAMS* psp)
  891. {
  892. return DPA_QuickSort2(0, psp->cp - 1, psp);
  893. }
  894. BOOL DPA_QuickSort2(int i, int j, SORTPARAMS* psp)
  895. {
  896. void** pp = psp->pp;
  897. LPARAM lParam = psp->lParam;
  898. PFNDPACOMPARE pfnCmp = psp->pfnCmp;
  899. int iPivot;
  900. void* pFirst;
  901. int k;
  902. int result;
  903. iPivot = -1;
  904. pFirst = pp[i];
  905. for (k = i + 1; k <= j; k++)
  906. {
  907. result = (*pfnCmp)(pp[k], pFirst, lParam);
  908. if (result > 0)
  909. {
  910. iPivot = k;
  911. break;
  912. }
  913. else if (result < 0)
  914. {
  915. iPivot = i;
  916. break;
  917. }
  918. }
  919. if (iPivot != -1)
  920. {
  921. int l = i;
  922. int r = j;
  923. void* pivot = pp[iPivot];
  924. do
  925. {
  926. void* p;
  927. p = pp[l];
  928. pp[l] = pp[r];
  929. pp[r] = p;
  930. while ((*pfnCmp)(pp[l], pivot, lParam) < 0)
  931. l++;
  932. while ((*pfnCmp)(pp[r], pivot, lParam) >= 0)
  933. r--;
  934. } while (l <= r);
  935. if (l - 1 > i)
  936. DPA_QuickSort2(i, l - 1, psp);
  937. if (j > l)
  938. DPA_QuickSort2(l, j, psp);
  939. }
  940. return TRUE;
  941. }
  942. #endif // USEQUICKSORT
  943. #ifdef USEHEAPSORT
  944. void DPA_HeapSortPushDown(int first, int last, SORTPARAMS* psp)
  945. {
  946. void** pp = psp->pp;
  947. LPARAM lParam = psp->lParam;
  948. PFNDPACOMPARE pfnCmp = psp->pfnCmp;
  949. int r;
  950. int r2;
  951. void* p;
  952. r = first;
  953. while (r <= last / 2)
  954. {
  955. int wRTo2R;
  956. r2 = r * 2;
  957. wRTo2R = (*pfnCmp)(pp[r-1], pp[r2-1], lParam);
  958. if (r2 == last)
  959. {
  960. if (wRTo2R < 0)
  961. {
  962. p = pp[r-1]; pp[r-1] = pp[r2-1]; pp[r2-1] = p;
  963. }
  964. break;
  965. }
  966. else
  967. {
  968. int wR2toR21 = (*pfnCmp)(pp[r2-1], pp[r2+1-1], lParam);
  969. if (wRTo2R < 0 && wR2toR21 >= 0)
  970. {
  971. p = pp[r-1]; pp[r-1] = pp[r2-1]; pp[r2-1] = p;
  972. r = r2;
  973. }
  974. else if ((*pfnCmp)(pp[r-1], pp[r2+1-1], lParam) < 0 && wR2toR21 < 0)
  975. {
  976. p = pp[r-1]; pp[r-1] = pp[r2+1-1]; pp[r2+1-1] = p;
  977. r = r2 + 1;
  978. }
  979. else
  980. {
  981. break;
  982. }
  983. }
  984. }
  985. }
  986. BOOL DPA_HeapSort(SORTPARAMS* psp)
  987. {
  988. void** pp = psp->pp;
  989. int c = psp->cp;
  990. int i;
  991. for (i = c / 2; i >= 1; i--)
  992. DPA_HeapSortPushDown(i, c, psp);
  993. for (i = c; i >= 2; i--)
  994. {
  995. void* p = pp[0]; pp[0] = pp[i-1]; pp[i-1] = p;
  996. DPA_HeapSortPushDown(1, i - 1, psp);
  997. }
  998. return TRUE;
  999. }
  1000. #endif // USEHEAPSORT
  1001. #if defined(MERGESORT)
  1002. #define SortCompare(psp, pp1, i1, pp2, i2) \
  1003. (psp->pfnCmp(pp1[i1], pp2[i2], psp->lParam))
  1004. //
  1005. // This function merges two sorted lists and makes one sorted list.
  1006. // psp->pp[iFirst, iFirst+cItes/2-1], psp->pp[iFirst+cItems/2, iFirst+cItems-1]
  1007. //
  1008. void DPA_MergeThem(SORTPARAMS* psp, int iFirst, int cItems)
  1009. {
  1010. //
  1011. // Notes:
  1012. // This function is separated from DPA_MergeSort2() to avoid comsuming
  1013. // stack variables. Never inline this.
  1014. //
  1015. int cHalf = cItems/2;
  1016. int iIn1, iIn2, iOut;
  1017. void **ppvSrc = &psp->pp[iFirst];
  1018. // Copy the first part to temp storage so we can write directly into
  1019. // the final buffer. Note that this takes at most psp->cp/2 DWORD's
  1020. CopyMemory(psp->ppT, ppvSrc, cHalf * sizeof(void*));
  1021. for (iIn1=0, iIn2=cHalf, iOut=0;;)
  1022. {
  1023. if (SortCompare(psp, psp->ppT, iIn1, ppvSrc, iIn2) <= 0) {
  1024. ppvSrc[iOut++] = psp->ppT[iIn1++];
  1025. if (iIn1==cHalf) {
  1026. // We used up the first half; the rest of the second half
  1027. // should already be in place
  1028. break;
  1029. }
  1030. } else {
  1031. ppvSrc[iOut++] = ppvSrc[iIn2++];
  1032. if (iIn2==cItems) {
  1033. // We used up the second half; copy the rest of the first half
  1034. // into place
  1035. CopyMemory(&ppvSrc[iOut], &psp->ppT[iIn1], (cItems-iOut)*sizeof(void *));
  1036. break;
  1037. }
  1038. }
  1039. }
  1040. }
  1041. //
  1042. // This function sorts a give list (psp->pp[iFirst,iFirst-cItems-1]).
  1043. //
  1044. void DPA_MergeSort2(SORTPARAMS* psp, int iFirst, int cItems)
  1045. {
  1046. //
  1047. // Notes:
  1048. // This function is recursively called. Therefore, we should minimize
  1049. // the number of local variables and parameters. At this point, we
  1050. // use one local variable and three parameters.
  1051. //
  1052. int cHalf;
  1053. switch(cItems)
  1054. {
  1055. case 1:
  1056. return;
  1057. case 2:
  1058. // Swap them, if they are out of order.
  1059. if (SortCompare(psp, psp->pp, iFirst, psp->pp, iFirst+1) > 0)
  1060. {
  1061. psp->ppT[0] = psp->pp[iFirst];
  1062. psp->pp[iFirst] = psp->pp[iFirst+1];
  1063. psp->pp[iFirst+1] = psp->ppT[0];
  1064. }
  1065. break;
  1066. default:
  1067. cHalf = cItems/2;
  1068. // Sort each half
  1069. DPA_MergeSort2(psp, iFirst, cHalf);
  1070. DPA_MergeSort2(psp, iFirst+cHalf, cItems-cHalf);
  1071. // Then, merge them.
  1072. DPA_MergeThem(psp, iFirst, cItems);
  1073. break;
  1074. }
  1075. }
  1076. BOOL DPA_MergeSort(SORTPARAMS* psp)
  1077. {
  1078. if (psp->cp==0)
  1079. return TRUE;
  1080. // Note that we divide by 2 below; we want to round down
  1081. psp->ppT = LocalAlloc(LPTR, psp->cp/2 * sizeof(void *));
  1082. if (!psp->ppT)
  1083. return FALSE;
  1084. DPA_MergeSort2(psp, 0, psp->cp);
  1085. LocalFree(psp->ppT);
  1086. return TRUE;
  1087. }
  1088. #endif // MERGESORT
  1089. // Search function
  1090. //
  1091. int WINAPI DPA_Search(HDPA pdpa, void* pFind, int iStart,
  1092. PFNDPACOMPARE pfnCompare, LPARAM lParam, UINT options)
  1093. {
  1094. int cp = DPA_GetPtrCount(pdpa);
  1095. ASSERT(pfnCompare);
  1096. ASSERT(0 <= iStart);
  1097. // Only allow these wierd flags if the list is sorted
  1098. ASSERT((options & DPAS_SORTED) || !(options & (DPAS_INSERTBEFORE | DPAS_INSERTAFTER)));
  1099. if (!(options & DPAS_SORTED))
  1100. {
  1101. // Not sorted: do linear search.
  1102. int i;
  1103. for (i = iStart; i < cp; i++)
  1104. {
  1105. if (0 == pfnCompare(pFind, DPA_FastGetPtr(pdpa, i), lParam))
  1106. return i;
  1107. }
  1108. return -1;
  1109. }
  1110. else
  1111. {
  1112. // Search the array using binary search. If several adjacent
  1113. // elements match the target element, the index of the first
  1114. // matching element is returned.
  1115. int iRet = -1; // assume no match
  1116. BOOL bFound = FALSE;
  1117. int nCmp = 0;
  1118. int iLow = 0; // Don't bother using iStart for binary search
  1119. int iMid = 0;
  1120. int iHigh = cp - 1;
  1121. // (OK for cp == 0)
  1122. while (iLow <= iHigh)
  1123. {
  1124. iMid = (iLow + iHigh) / 2;
  1125. nCmp = pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid), lParam);
  1126. if (0 > nCmp)
  1127. iHigh = iMid - 1; // First is smaller
  1128. else if (0 < nCmp)
  1129. iLow = iMid + 1; // First is larger
  1130. else
  1131. {
  1132. // Match; search back for first match
  1133. bFound = TRUE;
  1134. while (0 < iMid)
  1135. {
  1136. if (0 != pfnCompare(pFind, DPA_FastGetPtr(pdpa, iMid-1), lParam))
  1137. break;
  1138. else
  1139. iMid--;
  1140. }
  1141. break;
  1142. }
  1143. }
  1144. if (bFound)
  1145. {
  1146. ASSERT(0 <= iMid);
  1147. iRet = iMid;
  1148. }
  1149. // Did the search fail AND
  1150. // is one of the strange search flags set?
  1151. if (!bFound && (options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE)))
  1152. {
  1153. // Yes; return the index where the target should be inserted
  1154. // if not found
  1155. if (0 < nCmp) // First is larger
  1156. iRet = iLow;
  1157. else
  1158. iRet = iMid;
  1159. // (We don't distinguish between the two flags anymore)
  1160. }
  1161. else if ( !(options & (DPAS_INSERTAFTER | DPAS_INSERTBEFORE)) )
  1162. {
  1163. // Sanity check with linear search
  1164. ASSERT(DPA_Search(pdpa, pFind, iStart, pfnCompare, lParam, options & ~DPAS_SORTED) == iRet);
  1165. }
  1166. return iRet;
  1167. }
  1168. }
  1169. //===========================================================================
  1170. //
  1171. // String ptr management routines
  1172. //
  1173. // Copy as much of *psz to *pszBuf as will fit
  1174. //
  1175. // Warning: this same code is duplicated below.
  1176. //
  1177. int WINAPI Str_GetPtr(LPCTSTR pszCurrent, LPTSTR pszBuf, int cchBuf)
  1178. {
  1179. int cchToCopy;
  1180. if (!pszCurrent)
  1181. {
  1182. ASSERT(FALSE);
  1183. if (cchBuf > 0)
  1184. {
  1185. *pszBuf = TEXT('\0');
  1186. }
  1187. return 0;
  1188. }
  1189. cchToCopy = lstrlen(pszCurrent);
  1190. // if pszBuf is NULL, or they passed cchBuf = 0, return the needed buff size
  1191. if (!pszBuf || !cchBuf)
  1192. {
  1193. return cchToCopy + 1;
  1194. }
  1195. if (cchToCopy >= cchBuf)
  1196. {
  1197. cchToCopy = cchBuf - 1;
  1198. }
  1199. CopyMemory(pszBuf, pszCurrent, cchToCopy * sizeof(TCHAR));
  1200. pszBuf[cchToCopy] = TEXT('\0');
  1201. return cchToCopy + 1;
  1202. }
  1203. #ifdef DEBUG
  1204. //
  1205. // Str_GetPtr0 is just like Str_GetPtr except that it doesn't assert if
  1206. // pszCurrent = NULL.
  1207. //
  1208. int WINAPI Str_GetPtr0(LPCTSTR pszCurrent, LPTSTR pszBuf, int cchBuf)
  1209. {
  1210. return Str_GetPtr(pszCurrent ? pszCurrent : c_szNULL, pszBuf, cchBuf);
  1211. }
  1212. #endif
  1213. #ifdef UNICODE
  1214. //
  1215. // If we are build Unicode, then this is the ANSI version
  1216. // of the above function.
  1217. //
  1218. int WINAPI Str_GetPtrA(LPCSTR pszCurrent, LPSTR pszBuf, int cchBuf)
  1219. {
  1220. int cchToCopy;
  1221. if (!pszCurrent)
  1222. {
  1223. ASSERT(FALSE);
  1224. if (cchBuf > 0)
  1225. {
  1226. *pszBuf = '\0';
  1227. }
  1228. return 0;
  1229. }
  1230. cchToCopy = lstrlenA(pszCurrent);
  1231. // if pszBuf is NULL, or they passed cchBuf = 0, return the needed buff size
  1232. if (!pszBuf || !cchBuf)
  1233. {
  1234. return cchToCopy + 1;
  1235. }
  1236. if (cchToCopy >= cchBuf)
  1237. {
  1238. cchToCopy = cchBuf - 1;
  1239. }
  1240. CopyMemory(pszBuf, pszCurrent, cchToCopy * sizeof(CHAR));
  1241. pszBuf[cchToCopy] = TEXT('\0');
  1242. return cchToCopy + 1;
  1243. }
  1244. #else
  1245. //
  1246. // Unicode stub if this code is built ANSI
  1247. //
  1248. int WINAPI Str_GetPtrW(LPCWSTR psz, LPWSTR pszBuf, int cchBuf)
  1249. {
  1250. SetLastErrorEx(ERROR_CALL_NOT_IMPLEMENTED, SLE_WARNING);
  1251. return -1;
  1252. }
  1253. #endif
  1254. //
  1255. // This function is not exported.
  1256. //
  1257. BOOL Str_Set(LPTSTR *ppsz, LPCTSTR psz)
  1258. {
  1259. if (!psz || (psz == LPSTR_TEXTCALLBACK))
  1260. {
  1261. if (*ppsz)
  1262. {
  1263. if (*ppsz != (LPSTR_TEXTCALLBACK))
  1264. LocalFree(*ppsz);
  1265. }
  1266. *ppsz = (LPTSTR)psz;
  1267. }
  1268. else
  1269. {
  1270. LPTSTR pszNew = *ppsz;
  1271. UINT cbSize = (lstrlen(psz) + 1) * sizeof(TCHAR);
  1272. if (pszNew == LPSTR_TEXTCALLBACK)
  1273. pszNew = NULL;
  1274. pszNew = CCLocalReAlloc(pszNew, cbSize);
  1275. if (!pszNew)
  1276. return FALSE;
  1277. lstrcpy(pszNew, psz);
  1278. *ppsz = pszNew;
  1279. }
  1280. return TRUE;
  1281. }
  1282. // Set *ppszCurrent to a copy of pszNew, and free the previous value, if necessary
  1283. //
  1284. // WARNING: This same code is duplicated below
  1285. //
  1286. BOOL WINAPI Str_SetPtr(LPTSTR * ppszCurrent, LPCTSTR pszNew)
  1287. {
  1288. int cchLength;
  1289. LPTSTR pszOld;
  1290. LPTSTR pszNewCopy = NULL;
  1291. if (pszNew)
  1292. {
  1293. cchLength = lstrlen(pszNew);
  1294. // alloc a new buffer w/ room for the null terminator
  1295. pszNewCopy = (LPTSTR) Alloc((cchLength + 1) * sizeof(TCHAR));
  1296. if (!pszNewCopy)
  1297. return FALSE;
  1298. lstrcpyn(pszNewCopy, pszNew, cchLength + 1);
  1299. }
  1300. pszOld = InterlockedExchangePointer((void **)ppszCurrent, pszNewCopy);
  1301. if (pszOld)
  1302. Free(pszOld);
  1303. return TRUE;
  1304. }
  1305. #ifdef UNICODE
  1306. //
  1307. // ANSI stub when built Unicode.
  1308. //
  1309. BOOL WINAPI Str_SetPtrA(LPSTR * ppszCurrent, LPCSTR pszNew)
  1310. {
  1311. int cchLength;
  1312. LPSTR pszOld;
  1313. LPSTR pszNewCopy = NULL;
  1314. if (pszNew)
  1315. {
  1316. cchLength = lstrlenA(pszNew);
  1317. // alloc a new buffer w/ room for the null terminator
  1318. pszNewCopy = (LPSTR) Alloc((cchLength + 1) * sizeof(CHAR));
  1319. if (!pszNewCopy)
  1320. return FALSE;
  1321. lstrcpynA(pszNewCopy, pszNew, cchLength + 1);
  1322. }
  1323. pszOld = InterlockedExchangePointer((void **)ppszCurrent, pszNewCopy);
  1324. if (pszOld)
  1325. Free(pszOld);
  1326. return TRUE;
  1327. }
  1328. #else
  1329. // Unicode stub if this is built ANSI
  1330. BOOL WINAPI Str_SetPtrW(LPWSTR *ppwzCurrent, LPCWSTR pszNew)
  1331. {
  1332. int cchLength;
  1333. LPWSTR pwzOld;
  1334. LPWSTR pwzNewCopy = NULL;
  1335. if (pszNew)
  1336. {
  1337. cchLength = lstrlenW(pszNew); // Yes this is implemented on Win95.
  1338. // alloc a new buffer w/ room for the null terminator
  1339. pwzNewCopy = (LPWSTR) Alloc((cchLength + 1) * sizeof(WCHAR));
  1340. if (!pwzNewCopy)
  1341. return FALSE;
  1342. // lstrcpynW is thunked in unicwrap.cpp for Win95 machines.
  1343. StrCpyNW(pwzNewCopy, pszNew, cchLength + 1);
  1344. }
  1345. pwzOld = InterlockedExchangePointer((void **)ppwzCurrent, pwzNewCopy);
  1346. if (pwzOld)
  1347. Free(pwzOld);
  1348. return TRUE;
  1349. }
  1350. #endif