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.

687 lines
15 KiB

  1. //+------------------------------------------------------------------------
  2. //
  3. // File: memutil.cxx
  4. //
  5. // Contents: Memory utilities
  6. //
  7. // History: Stolen from Trident
  8. //
  9. //-------------------------------------------------------------------------
  10. #include "headers.hxx"
  11. EXTERN_C HANDLE g_hProcessHeap;
  12. #define SMALLBLOCKHEAP 0
  13. void
  14. ClearInterfaceFn(IUnknown **ppUnk)
  15. {
  16. IUnknown * pUnk;
  17. pUnk = *ppUnk;
  18. *ppUnk = NULL;
  19. if (pUnk)
  20. pUnk->Release();
  21. }
  22. //+------------------------------------------------------------------------
  23. // Allocation functions not implemented in this file:
  24. //
  25. // CDUTIL.HXX
  26. // operator new
  27. // operator delete
  28. //
  29. // OLE's OBJBASE.H
  30. // CoTaskMemAlloc, CoTaskMemFree
  31. //
  32. //-------------------------------------------------------------------------
  33. #if SMALLBLOCKHEAP
  34. DeclareTag(tagSmallBlockHeap, "!Memory", "Check small block heap every time")
  35. DeclareTag(tagSmallBlockHeapDisable, "!Memory", "Disable small block heap");
  36. #define _CRTBLD 1
  37. #include "winheap.h"
  38. EXTERN_C CRITICAL_SECTION g_csHeap;
  39. #if DBG == 1
  40. #define CHECKSBH if (IsTagEnabled(tagSmallBlockHeap)) {Assert(CheckSmallBlockHeap() && "Small block heap corrupt");};
  41. BOOL IsSmallBlockHeapEnabled()
  42. {
  43. static int g_fSmallBlockHeap = -1;
  44. if (g_fSmallBlockHeap == -1)
  45. g_fSmallBlockHeap = IsTagEnabled(tagSmallBlockHeapDisable) ? 0 : 1;
  46. return(g_fSmallBlockHeap == 1);
  47. }
  48. BOOL CheckSmallBlockHeap()
  49. {
  50. if (IsSmallBlockHeapEnabled())
  51. {
  52. EnterCriticalSection(&g_csHeap);
  53. BOOL f = __sbh_heap_check() >= 0;
  54. LeaveCriticalSection(&g_csHeap);
  55. return f;
  56. }
  57. return TRUE;
  58. }
  59. #else
  60. #define CHECKSBH
  61. #endif
  62. #else
  63. #if DBG == 1
  64. BOOL CheckSmallBlockHeap()
  65. {
  66. return TRUE;
  67. }
  68. #endif
  69. #endif SMALLBLOCKHEAP
  70. //+------------------------------------------------------------------------
  71. //
  72. // Function: _MemGetSize
  73. //
  74. // Synopsis: Get size of block allocated with MemAlloc/MemRealloc.
  75. //
  76. // Note that MemAlloc/MemRealloc can allocate more than
  77. // the requested number of bytes. Therefore the size returned
  78. // from this function is possibly greater than the size
  79. // passed to MemAlloc/Realloc.
  80. //
  81. // Arguments: [pv] - Return size of this block.
  82. //
  83. // Returns: The size of the block, or zero of pv == NULL.
  84. //
  85. //-------------------------------------------------------------------------
  86. ULONG
  87. _MemGetSize(void *pv)
  88. {
  89. if (pv == NULL)
  90. return 0;
  91. Assert(g_hProcessHeap);
  92. #if SMALLBLOCKHEAP
  93. #if DBG==1
  94. if (IsSmallBlockHeapEnabled())
  95. #endif
  96. {
  97. __sbh_region_t * preg;
  98. __sbh_page_t * ppage;
  99. __map_t * pmap;
  100. EnterCriticalSection(&g_csHeap);
  101. if ((pmap = __sbh_find_block(DbgPreGetSize(pv), &preg, &ppage)) != NULL )
  102. {
  103. size_t s = DbgPostGetSize(((size_t)(*pmap)) << _PARASHIFT);
  104. LeaveCriticalSection(&g_csHeap);
  105. return s;
  106. }
  107. LeaveCriticalSection(&g_csHeap);
  108. }
  109. #endif
  110. return DbgPostGetSize(HeapSize(g_hProcessHeap, 0, DbgPreGetSize(pv)));
  111. }
  112. //+------------------------------------------------------------------------
  113. //
  114. // Function: _MemAlloc
  115. //
  116. // Synopsis: Allocate block of memory.
  117. //
  118. // The contents of the block are undefined. If the requested size
  119. // is zero, this function returns a valid pointer. The returned
  120. // pointer is guaranteed to be suitably aligned for storage of any
  121. // object type.
  122. //
  123. // Arguments: [cb] - Number of bytes to allocate.
  124. //
  125. // Returns: Pointer to the allocated block, or NULL on error.
  126. //
  127. //-------------------------------------------------------------------------
  128. void *
  129. _MemAlloc(ULONG cb)
  130. {
  131. AssertSz (cb, "Requesting zero sized block.");
  132. // The small-block heap will lose its mind if this ever happens, so we
  133. // protect against the possibility.
  134. if (cb == 0)
  135. cb = 1;
  136. Assert(g_hProcessHeap);
  137. #if SMALLBLOCKHEAP
  138. #if DBG==1
  139. if (IsSmallBlockHeapEnabled())
  140. #endif
  141. {
  142. /* round up to the nearest paragraph */
  143. size_t cbr = (DbgPreAlloc(cb) + _PARASIZE - 1) & ~(_PARASIZE - 1);
  144. if (cbr < __sbh_threshold)
  145. {
  146. CHECKSBH;
  147. EnterCriticalSection(&g_csHeap);
  148. void * pv = DbgPostAlloc(__sbh_alloc_block(cbr >> _PARASHIFT));
  149. LeaveCriticalSection(&g_csHeap);
  150. if (pv)
  151. return pv;
  152. }
  153. }
  154. #endif
  155. return DbgPostAlloc(HeapAlloc(g_hProcessHeap, 0, DbgPreAlloc(cb)));
  156. }
  157. //+------------------------------------------------------------------------
  158. // Function: _MemAllocClear
  159. //
  160. // Synopsis: Allocate a zero filled block of memory.
  161. //
  162. // If the requested size is zero, this function returns a valid
  163. // pointer. The returned pointer is guaranteed to be suitably
  164. // aligned for storage of any object type.
  165. //
  166. // Arguments: [cb] - Number of bytes to allocate.
  167. //
  168. // Returns: Pointer to the allocated block, or NULL on error.
  169. //
  170. //-------------------------------------------------------------------------
  171. void *
  172. _MemAllocClear(ULONG cb)
  173. {
  174. AssertSz (cb, "Allocating zero sized block.");
  175. // The small-block heap will lose its mind if this ever happens, so we
  176. // protect against the possibility.
  177. if (cb == 0)
  178. cb = 1;
  179. void * pv;
  180. Assert(g_hProcessHeap);
  181. #if SMALLBLOCKHEAP
  182. #if DBG==1
  183. if (IsSmallBlockHeapEnabled())
  184. #endif
  185. {
  186. /* round up to the nearest paragraph */
  187. size_t cbr = (DbgPreAlloc(cb) + _PARASIZE - 1) & ~(_PARASIZE - 1);
  188. if (cbr < __sbh_threshold)
  189. {
  190. CHECKSBH;
  191. EnterCriticalSection(&g_csHeap);
  192. pv = DbgPostAlloc(__sbh_alloc_block(cbr >> _PARASHIFT));
  193. LeaveCriticalSection(&g_csHeap);
  194. if (pv)
  195. {
  196. memset(pv, 0, cb);
  197. return pv;
  198. }
  199. }
  200. }
  201. #endif
  202. pv = DbgPostAlloc(HeapAlloc(g_hProcessHeap, HEAP_ZERO_MEMORY,
  203. DbgPreAlloc(cb)));
  204. // In debug, DbgPostAlloc set the memory so we need to clear it again.
  205. #if DBG==1
  206. if (pv)
  207. {
  208. memset(pv, 0, cb);
  209. }
  210. #endif
  211. return pv;
  212. }
  213. //+------------------------------------------------------------------------
  214. //
  215. // Function: _MemFree
  216. //
  217. // Synopsis: Free a block of memory allocated with MemAlloc,
  218. // MemAllocFree or MemRealloc.
  219. //
  220. // Arguments: [pv] - Pointer to block to free. A value of zero is
  221. // is ignored.
  222. //
  223. //-------------------------------------------------------------------------
  224. void
  225. _MemFree(void *pv)
  226. {
  227. // The null check is required for HeapFree.
  228. if (pv == NULL)
  229. return;
  230. Assert(g_hProcessHeap);
  231. #if DBG == 1
  232. pv = DbgPreFree(pv);
  233. #endif
  234. #if SMALLBLOCKHEAP
  235. #if DBG==1
  236. if (IsSmallBlockHeapEnabled())
  237. #endif
  238. {
  239. __sbh_region_t *preg;
  240. __sbh_page_t * ppage;
  241. __map_t * pmap;
  242. CHECKSBH;
  243. EnterCriticalSection(&g_csHeap);
  244. if ( (pmap = __sbh_find_block(pv, &preg, &ppage)) != NULL ) {
  245. __sbh_free_block(preg, ppage, pmap);
  246. LeaveCriticalSection(&g_csHeap);
  247. DbgPostFree();
  248. return;
  249. }
  250. LeaveCriticalSection(&g_csHeap);
  251. }
  252. #endif
  253. HeapFree(g_hProcessHeap, 0, pv);
  254. DbgPostFree();
  255. }
  256. //+------------------------------------------------------------------------
  257. // Function: _MemRealloc
  258. //
  259. // Synopsis: Change the size of an existing block of memory, allocate a
  260. // block of memory, or free a block of memory depending on the
  261. // arguments.
  262. //
  263. // If cb is zero, this function always frees the block of memory
  264. // and *ppv is set to zero.
  265. //
  266. // If cb is not zero and *ppv is zero, then this function allocates
  267. // cb bytes.
  268. //
  269. // If cb is not zero and *ppv is non-zero, then this function
  270. // changes the size of the block, possibly by moving it.
  271. //
  272. // On error, *ppv is left unchanged. The block contents remains
  273. // unchanged up to the smaller of the new and old sizes. The
  274. // contents of the block beyond the old size is undefined.
  275. // The returned pointer is guaranteed to be suitably aligned for
  276. // storage of any object type.
  277. //
  278. // The signature of this function is different than thy typical
  279. // realloc-like function to avoid the following common error:
  280. // pv = realloc(pv, cb);
  281. // If realloc fails, then null is returned and the pointer to the
  282. // original block of memory is leaked.
  283. //
  284. // Arguments: [cb] - Requested size in bytes. A value of zero always frees
  285. // the block.
  286. // [ppv] - On input, pointer to existing block pointer or null.
  287. // On output, pointer to new block pointer.
  288. //
  289. // Returns: HRESULT
  290. //
  291. //-------------------------------------------------------------------------
  292. HRESULT
  293. _MemRealloc(void **ppv, ULONG cb)
  294. {
  295. void *pv;
  296. Assert(g_hProcessHeap);
  297. if (cb == 0)
  298. {
  299. _MemFree(*ppv);
  300. *ppv = 0;
  301. }
  302. else if (*ppv == NULL)
  303. {
  304. *ppv = _MemAlloc(cb);
  305. if (*ppv == NULL)
  306. return E_OUTOFMEMORY;
  307. }
  308. else
  309. {
  310. #if DBG == 1
  311. cb = DbgPreRealloc(*ppv, cb, &pv);
  312. #else
  313. pv = *ppv;
  314. #endif
  315. #if SMALLBLOCKHEAP
  316. #if DBG==1
  317. if (IsSmallBlockHeapEnabled())
  318. #endif
  319. {
  320. __sbh_region_t *preg;
  321. __sbh_page_t * ppage;
  322. __map_t * pmap;
  323. ULONG cbr;
  324. void * pvNew;
  325. cbr = (cb + _PARASIZE - 1) & ~(_PARASIZE - 1);
  326. CHECKSBH;
  327. EnterCriticalSection(&g_csHeap);
  328. if ( (pmap = __sbh_find_block(pv, &preg, &ppage)) != NULL )
  329. {
  330. pvNew = NULL;
  331. /*
  332. * If the new size falls below __sbh_threshold, try to
  333. * carry out the reallocation within the small-block
  334. * heap.
  335. */
  336. if ( cbr < __sbh_threshold ) {
  337. if ( __sbh_resize_block(preg, ppage, pmap, cbr >> _PARASHIFT))
  338. {
  339. pvNew = pv;
  340. }
  341. else if ((pvNew = __sbh_alloc_block(cbr >> _PARASHIFT)) != NULL)
  342. {
  343. ULONG cbOld = ((size_t)(*pmap)) << _PARASHIFT;
  344. memcpy(pvNew, pv, min(cbOld, cb));
  345. __sbh_free_block(preg, ppage, pmap);
  346. }
  347. }
  348. /*
  349. * If the reallocation has not been (successfully)
  350. * performed in the small-block heap, try to allocate a
  351. * new block with HeapAlloc.
  352. */
  353. if ((pvNew == NULL) && ((pvNew = HeapAlloc(g_hProcessHeap, 0, cb)) != NULL))
  354. {
  355. ULONG cbOld = ((size_t)(*pmap)) << _PARASHIFT;
  356. memcpy(pvNew, pv, min(cbOld, cb));
  357. __sbh_free_block(preg, ppage, pmap);
  358. }
  359. LeaveCriticalSection(&g_csHeap);
  360. *ppv = DbgPostRealloc(pvNew);
  361. if (*ppv)
  362. {
  363. return S_OK;
  364. }
  365. else
  366. {
  367. return E_OUTOFMEMORY;
  368. }
  369. }
  370. else
  371. {
  372. LeaveCriticalSection(&g_csHeap);
  373. }
  374. }
  375. #endif
  376. pv = DbgPostRealloc(HeapReAlloc(g_hProcessHeap, 0, pv, cb));
  377. if (pv == NULL)
  378. return E_OUTOFMEMORY;
  379. *ppv = pv;
  380. }
  381. return S_OK;
  382. }
  383. // MEMGUARD -------------------------------------------------------------------
  384. #if defined(MEMGUARD)
  385. #define MGGUARDDATA 0xF0F0BAAD
  386. struct MGGUARD
  387. {
  388. MGGUARD *pNext;
  389. DWORD dw;
  390. };
  391. MGGUARD * g_pMemList = NULL;
  392. void
  393. _MgMemValidate()
  394. {
  395. EnterCriticalSection(&g_csHeap);
  396. MGGUARD *pg = g_pMemList;
  397. while (pg)
  398. {
  399. if (pg->dw != MGGUARDDATA)
  400. {
  401. DebugBreak();
  402. }
  403. pg = pg->pNext;
  404. }
  405. LeaveCriticalSection(&g_csHeap);
  406. }
  407. void
  408. _MgRemove(MGGUARD *pmg)
  409. {
  410. if (!pmg)
  411. return;
  412. EnterCriticalSection(&g_csHeap);
  413. MGGUARD *pg = g_pMemList;
  414. if (pmg == pg)
  415. {
  416. g_pMemList = pg->pNext;
  417. goto Cleanup;
  418. }
  419. while (pg)
  420. {
  421. if (pg->pNext == pmg)
  422. {
  423. pg->pNext = pg->pNext->pNext;
  424. break;
  425. }
  426. pg = pg->pNext;
  427. }
  428. Cleanup:
  429. LeaveCriticalSection(&g_csHeap);
  430. }
  431. void
  432. _MgAdd(MGGUARD *pmg)
  433. {
  434. EnterCriticalSection(&g_csHeap);
  435. pmg->pNext = g_pMemList;
  436. g_pMemList = pmg;
  437. LeaveCriticalSection(&g_csHeap);
  438. }
  439. void *
  440. _MgMemAlloc(ULONG cb)
  441. {
  442. _MgMemValidate();
  443. MGGUARD * pmg = (MGGUARD *)_MemAlloc(sizeof(MGGUARD) + cb);
  444. if (pmg)
  445. {
  446. pmg->dw = MGGUARDDATA;
  447. _MgAdd(pmg);
  448. return(pmg + 1);
  449. }
  450. else
  451. {
  452. return(NULL);
  453. }
  454. }
  455. void *
  456. _MgMemAllocClear(ULONG cb)
  457. {
  458. _MgMemValidate();
  459. MGGUARD * pmg = (MGGUARD *)_MemAllocClear(sizeof(MGGUARD) + cb);
  460. if (pmg)
  461. {
  462. pmg->dw = MGGUARDDATA;
  463. _MgAdd(pmg);
  464. return(pmg + 1);
  465. }
  466. else
  467. {
  468. return(NULL);
  469. }
  470. }
  471. HRESULT
  472. _MgMemRealloc(void ** ppv, ULONG cb)
  473. {
  474. _MgMemValidate();
  475. if (cb == 0)
  476. {
  477. _MgMemFree(*ppv);
  478. *ppv = 0;
  479. return(S_OK);
  480. }
  481. if (*ppv == NULL)
  482. {
  483. *ppv = _MgMemAlloc(cb);
  484. return(*ppv ? S_OK : E_OUTOFMEMORY);
  485. }
  486. MGGUARD * pmg = (MGGUARD *)*ppv - 1;
  487. _MgRemove(pmg);
  488. HRESULT hr = _MemRealloc((void **)&pmg, sizeof(MGGUARD) + cb);
  489. if (hr == S_OK)
  490. {
  491. pmg->dw = MGGUARDDATA;
  492. _MgAdd(pmg);
  493. *ppv = pmg + 1;
  494. }
  495. return(hr);
  496. }
  497. ULONG
  498. _MgMemGetSize(void * pv)
  499. {
  500. _MgMemValidate();
  501. if (pv == NULL)
  502. return(0);
  503. else
  504. return(_MemGetSize((MGGUARD *)pv - 1) - sizeof(MGGUARD));
  505. }
  506. void
  507. _MgMemFree(void * pv)
  508. {
  509. _MgMemValidate();
  510. if (pv)
  511. {
  512. MGGUARD * pmg = (MGGUARD *)pv - 1;
  513. if (pmg->dw != MGGUARDDATA)
  514. {
  515. // The memory guard DWORD was overwritten! Bogus!
  516. #ifdef _M_IX86
  517. _asm int 3 // To get a proper stacktrace.
  518. #else
  519. DebugBreak();
  520. #endif
  521. }
  522. _MgRemove(pmg);
  523. _MemFree(pmg);
  524. }
  525. }
  526. HRESULT
  527. _MgMemAllocString(LPCTSTR pchSrc, LPTSTR * ppchDst)
  528. {
  529. TCHAR *pch;
  530. size_t cb;
  531. cb = (_tcsclen(pchSrc) + 1) * sizeof(TCHAR);
  532. *ppchDst = pch = (TCHAR *)_MgMemAlloc(cb);
  533. if (!pch)
  534. return E_OUTOFMEMORY;
  535. else
  536. {
  537. memcpy(pch, pchSrc, cb);
  538. return S_OK;
  539. }
  540. }
  541. HRESULT
  542. _MgMemAllocString(ULONG cch, const TCHAR *pchSrc, TCHAR **ppchDest)
  543. {
  544. TCHAR *pch;
  545. size_t cb = cch * sizeof(TCHAR);
  546. *ppchDest = pch = (TCHAR *)_MgMemAlloc(cb + sizeof(TCHAR));
  547. if (!pch)
  548. return E_OUTOFMEMORY;
  549. else
  550. {
  551. memcpy(pch, pchSrc, cb);
  552. pch[cch] = 0;
  553. return S_OK;
  554. }
  555. }
  556. HRESULT
  557. _MgMemReplaceString(const TCHAR *pchSrc, TCHAR **ppchDest)
  558. {
  559. HRESULT hr;
  560. TCHAR *pch;
  561. if (pchSrc)
  562. {
  563. hr = THR(_MgMemAllocString(pchSrc, &pch));
  564. if (hr)
  565. RRETURN(hr);
  566. }
  567. else
  568. {
  569. pch = NULL;
  570. }
  571. _MgMemFreeString(*ppchDest);
  572. *ppchDest = pch;
  573. return S_OK;
  574. }
  575. #endif // MEMGUARD