Windows NT 4.0 source code leak
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.

968 lines
22 KiB

4 years ago
  1. /************************************************************************
  2. * *
  3. * LCMEM.CPP *
  4. * *
  5. * Copyright (C) Microsoft Corporation 1994 *
  6. * All Rights reserved. *
  7. * *
  8. ************************************************************************/
  9. #ifdef SPECIFICATION
  10. This module contains heap management code. When memory is freed, there
  11. is an automatic check for an overwrite of the allocated memory.
  12. Allocations are always aligned on 32-bit boundarys for Intel, and 64-bit
  13. boundarys for MIPS. No "hole" is left that is smaller then the alignment
  14. type (32 bytes for INTEL) in order to cut down on fragmentation.
  15. The fsStatus has a dual purpose -- it not only indicates if the
  16. block of memory is in use or not, but it is also used to check for heap
  17. corruption. If someone mallocs a block of memory, and then writes over
  18. the end of that allocation, the heap will be corrupted -- this can be
  19. determined if the fsStatus is not one of the two valid values.
  20. Other automatic checks are for attempting to free or reallocate a NULL
  21. pointer, or freeing or reallocaing a pointer that is not in the range
  22. of the heap.
  23. Heap size is limited to 1 meg. To get more, raise MAX_HEAP. In the
  24. _DEBUG version, the complete heap is checked for consistency when the
  25. program exits. In both retail and debug, the entire heap is freed prior
  26. to exiting.
  27. Note that it is unnecessary to check return values for lcMalloc(),
  28. lcCalloc(), and lcReAlloc(). If a problem occurs, such as a lack of
  29. system memory, these functions simply won't return.
  30. I haven't tested this against the 32-bit C runtime versions, but the
  31. 16-bit version of this code was 5-10 times faster then the C runtime
  32. equivalents.
  33. REQUIRED FUNCTIONS:
  34. OOM(void)
  35. The caller must supply an OOM() function that will be called if
  36. memory allocation fails. It is assumed that the OOM() function
  37. will not return.
  38. AssertErrorReport(const char* szBuf, int usLine, const char* pszFile);
  39. Retail function for reporting internal errors.
  40. RECOMENDATION: If you're going to allocate memory that will be rarely
  41. discarded, then either use the C runtime, or allocate
  42. it as soon as possible. This heap manager maintains a
  43. pointer to the first available block in the heap, so
  44. there is no speed penalty for memory objects allocated
  45. before any object that gets freed. Use LocalAlloc for
  46. large objects. Use this heap management for memory which
  47. might get corrupted, or that will be allocated and
  48. freed.
  49. COMMENTS: One could turn all of this into a class if you wanted multiple
  50. heaps. The price would be the overhead of passing the class
  51. pointer every time one of the class functions was called.
  52. #endif
  53. #include "stdafx.h"
  54. #pragma hdrstop
  55. #ifndef LCMEM_H
  56. #include "lcmem.h"
  57. #endif
  58. #ifdef _DEBUG
  59. #undef THIS_FILE
  60. static char THIS_FILE[] = __FILE__;
  61. #endif
  62. enum HEAP_STATUS {
  63. INUSE = 0x13571357, // You look at memory in hex. Make the patterns pretty
  64. UNUSED = 0x24682468 // In Hex.
  65. };
  66. typedef struct _heap {
  67. HEAP_STATUS fsStatus;
  68. int cb;
  69. _heap* pNext;
  70. _heap* pPrev;
  71. } HEAP;
  72. // Align on 32 bits for Intel, 64 bits for MIPS
  73. #ifdef _X86_
  74. #ifdef _DEBUG
  75. int ALIGNMENT = 4; // change to 1 for better checking for overwrite
  76. #else
  77. const int ALIGNMENT = 4;
  78. #endif // _DEBUG
  79. #else
  80. const int ALIGNMENT = 8;
  81. #endif
  82. const int MINIMUM_LEFTOVER = ((sizeof(HEAP) + 32) / \
  83. ALIGNMENT * ALIGNMENT + ALIGNMENT);
  84. const int MAX_HEAP = (1024 * 1024 * 10); // maximum size of the heap = 10 Meg
  85. enum {
  86. HEAP_CORRUPTED,
  87. HEAP_ALREADY_FREED,
  88. HEAP_CHECK_FAILURE,
  89. HEAP_INVALID_POINTER,
  90. HEAP_NULL_POINTER,
  91. };
  92. const int HEAP_ALLOC_INCREMENT = (64 * 1024);
  93. int curHeapAllocation = HEAP_ALLOC_INCREMENT;
  94. #ifndef _DEBUG
  95. #define pszCallersFile __FILE__
  96. #define line __LINE__
  97. #endif
  98. static HEAP* pHeapBase;
  99. static HEAP* pHeapAlloc;
  100. static HEAP* pHeapFirstAvail;
  101. #ifdef _DEBUG
  102. static BOOL fCorupted;
  103. #endif
  104. static void STDCALL CorruptedHeap(int HeapError, const char* pszFile, int Line);
  105. // We create a class so that the heap will be automatically initialized
  106. // before program execution begins.
  107. class CInitHeap
  108. {
  109. public:
  110. CInitHeap();
  111. ~CInitHeap();
  112. };
  113. /*
  114. * REVIEW: The advantage of initialization here is that the app linking with
  115. * this module doesn't need to know or care about how to initialize the heap.
  116. * The downside is that if initialization fails (typically, out of memory),
  117. * then we can't load any strings from the resource table because we don't
  118. * have an instance handle yet. HCW's OOM() function hard-codes an English
  119. * message. This may not be acceptable for other apps.
  120. */
  121. static CInitHeap theap;
  122. /***************************************************************************
  123. FUNCTION: CInitHeap
  124. PURPOSE: Reserve memory for the heap, and initialize the first block
  125. COMMENTS:
  126. MODIFICATION DATES:
  127. 23-Feb-1991 [ralphw]
  128. 01-Jun-1994 [ralphw] Ported to 32-bits
  129. ***************************************************************************/
  130. CInitHeap::CInitHeap()
  131. {
  132. pHeapAlloc = (HEAP*) VirtualAlloc(NULL, MAX_HEAP, MEM_RESERVE, PAGE_READWRITE);
  133. if (!pHeapAlloc)
  134. OOM();
  135. if (!VirtualAlloc(pHeapAlloc, curHeapAllocation, MEM_COMMIT, PAGE_READWRITE))
  136. OOM();
  137. // The first structure gives information about the entire heap
  138. pHeapAlloc->fsStatus = INUSE;
  139. pHeapAlloc->cb = curHeapAllocation;
  140. pHeapAlloc->pNext = (HEAP*) (((PBYTE) pHeapAlloc) + sizeof(HEAP));
  141. pHeapAlloc->pPrev = NULL;
  142. pHeapBase = pHeapAlloc;
  143. pHeapBase++;
  144. pHeapBase->fsStatus = UNUSED;
  145. pHeapBase->cb = curHeapAllocation - (2 * sizeof(HEAP));
  146. pHeapBase->pNext = NULL;
  147. pHeapBase->pPrev = NULL;
  148. pHeapFirstAvail = pHeapBase;
  149. }
  150. CInitHeap::~CInitHeap()
  151. {
  152. if (pHeapAlloc) {
  153. #ifdef _DEBUG
  154. if (!fCorupted)
  155. lcHeapCheck();
  156. #endif
  157. // Decommit all regions and then release them
  158. VirtualFree(pHeapAlloc, MAX_HEAP, MEM_DECOMMIT);
  159. VirtualFree(pHeapAlloc, 0, MEM_RELEASE);
  160. pHeapAlloc = NULL; // total paranoia
  161. }
  162. }
  163. /***************************************************************************
  164. FUNCTION: tmalloc
  165. PURPOSE: Allocate memory. Size of memory will be rounded up to
  166. ALIGNMENT
  167. RETURNS:
  168. COMMENTS:
  169. MODIFICATION DATES:
  170. 16-Feb-1991 [ralphw]
  171. ***************************************************************************/
  172. #ifdef _DEBUG
  173. void* STDCALL tmalloc(int cb, int line, const char* pszCallersFile)
  174. #else
  175. void* STDCALL tmalloc(int cb)
  176. #endif
  177. {
  178. HEAP* pNext;
  179. #ifdef CODEVIEW
  180. NHP pPrev;
  181. #endif
  182. cb += sizeof(HEAP);
  183. int cbAligned = (cb & (ALIGNMENT - 1)) ? // aligned on a paragraph?
  184. (cb += sizeof(HEAP)) / ALIGNMENT * ALIGNMENT + ALIGNMENT :
  185. cb;
  186. int cbReasonable = cbAligned + MINIMUM_LEFTOVER;
  187. HEAP* pheap = pHeapFirstAvail;
  188. ASSERT(pheap >= pHeapBase && (PBYTE) pheap <=
  189. (PBYTE) pHeapAlloc + curHeapAllocation);
  190. for (;;) {
  191. if (pheap->fsStatus == INUSE) {
  192. NextLink:
  193. // Warning! we can be here due to evil goto, and fsStatus may not
  194. // be INUSE!
  195. if (pheap->pNext != NULL) {
  196. pheap = pheap->pNext;
  197. }
  198. else {
  199. int add;
  200. if (pheap->fsStatus == UNUSED) {
  201. add = 0;
  202. do {
  203. add += HEAP_ALLOC_INCREMENT;
  204. } while (pheap->cb + add < cbReasonable);
  205. }
  206. else
  207. add = HEAP_ALLOC_INCREMENT;
  208. if (curHeapAllocation + add > MAX_HEAP)
  209. OOM();
  210. // Commit more memory, and then update curHeapAllocation.
  211. if (!VirtualAlloc((PBYTE) pHeapAlloc + curHeapAllocation,
  212. add, MEM_COMMIT, PAGE_READWRITE))
  213. OOM();
  214. curHeapAllocation += add;
  215. if (pheap->fsStatus == INUSE) {
  216. // Create a new heap element and attach it.
  217. HEAP* pTmp = (HEAP*) ((PBYTE) pHeapAlloc +
  218. (curHeapAllocation - HEAP_ALLOC_INCREMENT - sizeof(HEAP)));
  219. pTmp->fsStatus = UNUSED;
  220. pTmp->cb = add - (2 * sizeof(HEAP));
  221. pTmp->pNext = NULL;
  222. pTmp->pPrev = pheap;
  223. pheap->pNext = pTmp;
  224. pheap = pheap->pNext;
  225. }
  226. else {
  227. pheap->cb += add;
  228. }
  229. }
  230. continue;
  231. }
  232. else if (pheap->fsStatus == UNUSED) {
  233. /*
  234. * We put this check here with a jump in order to be certain
  235. * to check the fsStatus (which will tell us if the heap
  236. * gets corrupted).
  237. */
  238. if (cbAligned == pheap->cb) { // perfect fit?
  239. pheap->fsStatus = INUSE;
  240. if (pheap > pHeapFirstAvail)
  241. pHeapFirstAvail = pheap;
  242. return (void *) ((PBYTE) pheap + sizeof(HEAP));
  243. }
  244. /*
  245. * We don't want to leave little blocks of free memory, since
  246. * typically we won't be able to fill them. So, we use
  247. * cbReasonable to determine if we have a reasonable fit.
  248. */
  249. if (cbReasonable > pheap->cb)
  250. goto NextLink;
  251. /*
  252. * If the requested size is smaller then the block available,
  253. * then we need to create a new heap pointer to the next free
  254. * block. We already checked for an exact fit.
  255. */
  256. if (cbAligned < pheap->cb) {
  257. pNext = (HEAP*) (((PBYTE) pheap) + cbAligned);
  258. pNext->fsStatus = UNUSED;
  259. pNext->cb = pheap->cb - cbAligned;
  260. pNext->pPrev = pheap;
  261. pNext->pNext = pheap->pNext;
  262. pheap->pNext = pNext;
  263. /*
  264. * We've created a new block, between two blocks. We must
  265. * reset the next block's previous pointer to the new block.
  266. */
  267. pNext = pNext->pNext;
  268. if (pNext)
  269. pNext->pPrev = pheap->pNext;
  270. }
  271. pheap->fsStatus = INUSE;
  272. pheap->cb = cbAligned;
  273. if (pheap > pHeapFirstAvail)
  274. pHeapFirstAvail = pheap;
  275. return (void *) ((PBYTE) pheap + sizeof(HEAP));
  276. }
  277. else {
  278. CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
  279. }
  280. }
  281. }
  282. #ifdef _DEBUG
  283. void* STDCALL tcalloc(int cb, int line, const char* pszCallersFile)
  284. #else
  285. void* STDCALL tcalloc(int cb)
  286. #endif
  287. {
  288. #ifdef _DEBUG
  289. void* pv = tmalloc(cb, line, pszCallersFile);
  290. #else
  291. void* pv = tmalloc(cb);
  292. #endif
  293. /*
  294. * We need to clear the memory beyond the end of the allocation
  295. * in case we realloc this to a larger block.
  296. */
  297. cb += sizeof(HEAP);
  298. int cbAligned = (cb & (ALIGNMENT - 1)) ? // aligned on a paragraph?
  299. (cb += sizeof(HEAP)) / ALIGNMENT * ALIGNMENT + ALIGNMENT :
  300. cb;
  301. memset(pv, 0, cbAligned - sizeof(HEAP));
  302. return pv;
  303. }
  304. /***************************************************************************
  305. FUNCTION: tfree
  306. PURPOSE: Free a block of memory
  307. RETURNS:
  308. COMMENTS:
  309. In the process of freeing the block, check for a corrupted heap,
  310. and combine free blocks.
  311. MODIFICATION DATES:
  312. 16-Feb-1991 [ralphw]
  313. ***************************************************************************/
  314. #ifdef _DEBUG
  315. void STDCALL tfree(void *pv, int line, const char* pszCallersFile)
  316. #else
  317. void STDCALL tfree(void *pv)
  318. #endif
  319. {
  320. HEAP* pNext;
  321. if (!pv)
  322. CorruptedHeap(HEAP_NULL_POINTER, pszCallersFile, line);
  323. HEAP* pheap = (HEAP*) ((PBYTE) pv - sizeof(HEAP));
  324. if ((PBYTE) pheap > (((PBYTE) pHeapAlloc) + curHeapAllocation) ||
  325. pheap < pHeapAlloc)
  326. CorruptedHeap(HEAP_INVALID_POINTER, pszCallersFile, line);
  327. if (pheap->fsStatus != INUSE) {
  328. if (pheap->fsStatus == UNUSED) {
  329. CorruptedHeap(HEAP_ALREADY_FREED, pszCallersFile, line);
  330. }
  331. else {
  332. CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
  333. }
  334. }
  335. #ifdef _DEBUG
  336. /*
  337. * Deliberately and with malice of forethought, trash the freed memory
  338. * so that if anyone tries to use it again, they'll get bad results.
  339. */
  340. FillMemory(pv, pheap->cb - sizeof(HEAP), '6');
  341. #endif
  342. pheap->fsStatus = UNUSED;
  343. HEAP* pPrev;
  344. // Try to combine any previous free blocks
  345. if ((pPrev = (HEAP*) pheap->pPrev) != NULL) {
  346. if (pPrev->pNext != pheap) {
  347. CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
  348. }
  349. // Combine all previous free blocks
  350. if (pPrev->fsStatus == UNUSED) {
  351. do {
  352. pPrev->cb += pheap->cb;
  353. pPrev->pNext = pheap->pNext;
  354. pheap = pPrev;
  355. if ((pPrev = (HEAP*) pheap->pPrev) == NULL)
  356. break;
  357. } while (pPrev->fsStatus == UNUSED);
  358. if ((pNext = (HEAP*) pheap->pNext) != NULL)
  359. pNext->pPrev = pheap;
  360. if (pPrev != NULL && pPrev->pNext != pheap) {
  361. CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
  362. }
  363. }
  364. else if (pPrev->fsStatus != INUSE) {
  365. /*
  366. * If we get here, the freed block has data in it that extended
  367. * past the original malloc'd size.
  368. */
  369. CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
  370. }
  371. }
  372. if (pheap < pHeapFirstAvail)
  373. pHeapFirstAvail = pheap;
  374. ASSERT(pHeapFirstAvail->fsStatus == UNUSED ||
  375. pHeapFirstAvail->fsStatus == INUSE);
  376. // Try to combine with any following free blocks
  377. if ((pNext = (HEAP*) pheap->pNext) != NULL) {
  378. // Combine all following free blocks
  379. if (pNext->fsStatus == UNUSED) {
  380. do {
  381. pheap->cb += pNext->cb;
  382. pheap->pNext = pNext->pNext;
  383. // Is this the end of the heap?
  384. if ((pNext = (HEAP*) pheap->pNext) == NULL) {
  385. ASSERT(pHeapFirstAvail->fsStatus == UNUSED ||
  386. pHeapFirstAvail->fsStatus == INUSE);
  387. // Decommit excess memory.
  388. if (pheap->cb > (80 * 1024)) {
  389. int cbNewAllocation = curHeapAllocation;
  390. while (pheap->cb > (80 * 1024)) {
  391. pheap->cb -= (64 * 1024);
  392. cbNewAllocation -= (64 * 1024);
  393. }
  394. VirtualFree((PBYTE) pHeapAlloc + cbNewAllocation,
  395. curHeapAllocation - cbNewAllocation,
  396. MEM_DECOMMIT);
  397. curHeapAllocation = cbNewAllocation;
  398. }
  399. return;
  400. }
  401. } while (pNext->fsStatus == UNUSED);
  402. pNext->pPrev = pheap;
  403. return;
  404. }
  405. else if (pNext->fsStatus != INUSE) {
  406. /*
  407. * If we get here, the freed block has data in it that extended
  408. * past the original malloc'd size.
  409. */
  410. CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
  411. }
  412. }
  413. }
  414. /***************************************************************************
  415. FUNCTION: tclearfree
  416. PURPOSE: Variation of tfree, this one sets the caller's pointer to
  417. NULL.
  418. PARAMETERS:
  419. *pv
  420. RETURNS:
  421. COMMENTS:
  422. MODIFICATION DATES:
  423. 22-Apr-1994 [ralphw]
  424. ***************************************************************************/
  425. #ifdef _DEBUG
  426. void STDCALL tclearfree(void **pv, int line, const char* pszCallersFile)
  427. #else
  428. void STDCALL tclearfree(void **pv)
  429. #endif
  430. {
  431. #ifdef _DEBUG
  432. tfree(*pv, line, pszCallersFile);
  433. #else
  434. tfree(*pv);
  435. #endif
  436. *pv = NULL;
  437. }
  438. /***************************************************************************
  439. FUNCTION: theapcheck
  440. PURPOSE: Checks the heap for possible corruption
  441. RETURNS:
  442. COMMENTS:
  443. MODIFICATION DATES:
  444. 23-Feb-1991 [ralphw]
  445. ***************************************************************************/
  446. #ifdef _DEBUG
  447. void STDCALL theapcheck(int line, const char* pszCallersFile)
  448. #else
  449. void STDCALL theapcheck(void)
  450. #endif
  451. {
  452. HEAP* pheap = pHeapAlloc->pNext;
  453. HEAP *pPrev;
  454. if (!(pHeapFirstAvail->fsStatus == UNUSED ||
  455. pHeapFirstAvail->fsStatus == INUSE))
  456. CorruptedHeap(HEAP_CHECK_FAILURE, pszCallersFile, line);
  457. do {
  458. pPrev = pheap->pPrev;
  459. if ((pheap->fsStatus != INUSE && pheap->fsStatus != UNUSED))
  460. CorruptedHeap(HEAP_CHECK_FAILURE, pszCallersFile, line);
  461. #ifdef _DEBUG
  462. /*
  463. * REVIEW: This stuff checks heap management code -- it should be removed
  464. * once the heap management code is reliable.
  465. */
  466. if (pPrev)
  467. ASSERT(pPrev->pNext == pheap);
  468. if (pheap->pNext)
  469. ASSERT(((UINT) pheap) + pheap->cb == (UINT) pheap->pNext);
  470. #endif
  471. pheap = (HEAP*) pheap->pNext;
  472. } while (pheap != NULL);
  473. }
  474. /***************************************************************************
  475. FUNCTION: CorruptedHeap
  476. PURPOSE: Report the type of heap error, and which heap it occurred in
  477. RETURNS:
  478. COMMENTS:
  479. MODIFICATION DATES:
  480. 23-Feb-1991 [ralphw]
  481. ***************************************************************************/
  482. static void STDCALL CorruptedHeap(int HeapError, const char* pszFile, int usLine)
  483. {
  484. char szBuf[256];
  485. #ifdef _DEBUG
  486. fCorupted = TRUE;
  487. #endif
  488. switch(HeapError) {
  489. case HEAP_ALREADY_FREED:
  490. strcpy(szBuf, "Pointer already freed");
  491. break;
  492. case HEAP_CHECK_FAILURE:
  493. strcpy(szBuf, "Heap check failed (corrupted heap)");
  494. break;
  495. case HEAP_INVALID_POINTER:
  496. strcpy(szBuf, "Attempt to free a non-heap pointer");
  497. break;
  498. case HEAP_CORRUPTED:
  499. default:
  500. strcpy(szBuf, "Heap is corrupted");
  501. break;
  502. }
  503. AssertErrorReport(szBuf, usLine, pszFile);
  504. }
  505. /***************************************************************************
  506. FUNCTION: lcSize
  507. PURPOSE: Determine the actual size of the memory allocation. This
  508. may be larger then was originally requested due to
  509. alignment issues.
  510. RETURNS:
  511. COMMENTS:
  512. MODIFICATION DATES:
  513. 29-May-1991 [ralphw]
  514. ***************************************************************************/
  515. int STDCALL lcSize(void* pv)
  516. {
  517. if (!pv)
  518. return 0;
  519. HEAP* pheap = (HEAP*) ((PBYTE) pv - sizeof(HEAP));
  520. if ((PBYTE) pheap > (((PBYTE) pHeapAlloc) + curHeapAllocation) ||
  521. pheap < pHeapAlloc)
  522. return 0;
  523. if (pheap->fsStatus != INUSE)
  524. return 0;
  525. return pheap->cb - sizeof(HEAP);
  526. }
  527. PSTR STDCALL lcStrDup(const char* psz)
  528. {
  529. PSTR pszDup = (PSTR) lcMalloc(strlen(psz) + 1);
  530. return strcpy(pszDup, psz);
  531. }
  532. /***************************************************************************
  533. FUNCTION: tRealloc
  534. PURPOSE:
  535. PARAMETERS:
  536. pv
  537. cbNew
  538. line
  539. pszCallersFile
  540. RETURNS:
  541. COMMENTS:
  542. MODIFICATION DATES:
  543. 20-Mar-1994 [ralphw]
  544. ***************************************************************************/
  545. #ifdef _DEBUG
  546. void* STDCALL trealloc(void* pv, int cbNew, int line, const char* pszCallersFile)
  547. #else
  548. void* STDCALL trealloc(void* pv, int cbNew)
  549. #endif
  550. {
  551. if (!pv)
  552. CorruptedHeap(HEAP_NULL_POINTER, pszCallersFile, line);
  553. HEAP* pheap = (HEAP*) ((PBYTE) pv - sizeof(HEAP));
  554. if ((PBYTE) pheap > (((PBYTE) pHeapAlloc) + curHeapAllocation) ||
  555. pheap < pHeapAlloc)
  556. CorruptedHeap(HEAP_INVALID_POINTER, pszCallersFile, line);
  557. if (pheap->fsStatus != INUSE) {
  558. if (pheap->fsStatus == UNUSED) {
  559. CorruptedHeap(HEAP_ALREADY_FREED, pszCallersFile, line);
  560. }
  561. else {
  562. CorruptedHeap(HEAP_CORRUPTED, pszCallersFile, line);
  563. }
  564. }
  565. cbNew += sizeof(HEAP);
  566. int cbAligned = (cbNew & (ALIGNMENT - 1)) ?
  567. (cbNew += sizeof(HEAP)) / ALIGNMENT * ALIGNMENT + ALIGNMENT :
  568. cbNew;
  569. if (cbAligned < MINIMUM_LEFTOVER)
  570. cbAligned = MINIMUM_LEFTOVER;
  571. /*
  572. * If we're not going to actually change the size, then just return.
  573. * Note that we never reduce memory below cbAligned. This keeps us
  574. * aligned and reduces heap fragmentation.
  575. */
  576. if (cbAligned == pheap->cb)
  577. return(pv);
  578. // Are we reducing the size?
  579. if (cbAligned < pheap->cb) {
  580. // If the resultant free block would be less then MINIMUM_LEFTOVER
  581. // then we do nothing. We don't want small holes lying around.
  582. if (pheap->cb - cbAligned < MINIMUM_LEFTOVER)
  583. return pv;
  584. int cbDiff = pheap->cb - cbAligned;
  585. pheap->cb = cbAligned;
  586. if (pheap->pNext->fsStatus == UNUSED) {
  587. // Next block is free, so just move it down
  588. HEAP* pheapNext = (HEAP*) ((PBYTE) pheap + cbAligned);
  589. memmove(pheapNext, pheap->pNext, sizeof(HEAP));
  590. pheap->pNext = pheapNext;
  591. pheapNext->cb += cbDiff;
  592. pheapNext->pPrev = pheap;
  593. if (pheapNext->pNext)
  594. pheapNext->pNext->pPrev = pheapNext;
  595. return pv;
  596. }
  597. else {
  598. HEAP* pheapNext = (HEAP*) ((PBYTE) pheap + cbAligned);
  599. pheapNext->fsStatus = UNUSED;
  600. pheapNext->cb = cbDiff;
  601. pheapNext->pPrev = pheap;
  602. pheapNext->pNext = pheap->pNext;
  603. pheap->pNext->pPrev = pheapNext;
  604. pheap->pNext = pheapNext;
  605. return pv;
  606. }
  607. }
  608. // If we get here, we're increasing the size.
  609. int cbDiff = cbAligned - pheap->cb;
  610. if (pheap->pNext && pheap->pNext->fsStatus == UNUSED &&
  611. (pheap->pNext->cb == cbDiff ||
  612. pheap->pNext->cb > cbDiff + MINIMUM_LEFTOVER)) {
  613. int clear = cbAligned - pheap->cb; // amount of memory to clear
  614. pheap->cb = cbAligned;
  615. if (pheap->pNext->cb == cbDiff) { // an exact match, blast the block
  616. void* pvClear = pheap->pNext;
  617. HEAP* pheapNext = pheap->pNext;
  618. pheap->pNext = pheapNext->pNext;
  619. if (pheap->pNext)
  620. pheapNext->pNext->pPrev = pheap;
  621. memset(pvClear, 0, clear);
  622. return pv;
  623. }
  624. else { // move the next block and adjust
  625. void* pvClear = pheap->pNext;
  626. HEAP* pheapNext = (HEAP*) ((PBYTE) pheap + cbAligned);
  627. memmove(pheapNext, pheap->pNext, sizeof(HEAP));
  628. pheapNext->cb -= cbDiff;
  629. if (pHeapFirstAvail == pheap->pNext)
  630. pHeapFirstAvail = pheapNext;
  631. pheap->pNext = pheapNext;
  632. if (pheapNext->pNext)
  633. pheapNext->pNext->pPrev = pheapNext;
  634. memset(pvClear, 0, clear);
  635. return pv;
  636. }
  637. }
  638. // No room for expansion, so we must move the entire block.
  639. // REVIEW: marginally faster to simply zero out the new memory rather
  640. // then the entire block
  641. PBYTE pbNew = (PBYTE) lcCalloc(cbAligned - sizeof(HEAP));
  642. memcpy(pbNew, pv, pheap->cb - sizeof(HEAP));
  643. lcFree(pv);
  644. return pbNew;
  645. }
  646. #define HEAP_THRESHOLD (16 * 1024) // below this threshold, use local heap
  647. CMem::CMem(int size)
  648. {
  649. if (size < HEAP_THRESHOLD) {
  650. fLocal = TRUE;
  651. pb = (PBYTE) lcMalloc(size);
  652. }
  653. else {
  654. fLocal = FALSE;
  655. pb = (PBYTE) LocalAlloc(LMEM_FIXED, size);
  656. }
  657. psz = (PSTR) pb;
  658. if (!pb)
  659. OOM();
  660. };
  661. CMem::~CMem(void)
  662. {
  663. if (fLocal)
  664. lcFree(pb);
  665. else
  666. LocalFree((HLOCAL) pb);
  667. }
  668. void CMem::resize(int cb)
  669. {
  670. if (fLocal)
  671. lcReAlloc(pb, cb);
  672. else
  673. LocalReAlloc((HLOCAL) pb, cb, LMEM_ZEROINIT);
  674. }
  675. void STDCALL lcReport(PSTR pszReport)
  676. {
  677. HEAP* pheap = pHeapAlloc->pNext;
  678. HEAP *pPrev;
  679. if (!(pHeapFirstAvail->fsStatus == UNUSED ||
  680. pHeapFirstAvail->fsStatus == INUSE))
  681. CorruptedHeap(HEAP_CHECK_FAILURE, __FILE__, __LINE__);
  682. int cbAllocated = 0, cbDeAllocated = 0;
  683. int cAllocated = 0, cDeAllocated = 0;
  684. do {
  685. pPrev = pheap->pPrev;
  686. if (pheap->fsStatus == INUSE) {
  687. cbAllocated += pheap->cb;
  688. cAllocated++;
  689. }
  690. else if (pheap->fsStatus == UNUSED) {
  691. cbDeAllocated += pheap->cb;
  692. cDeAllocated++;
  693. }
  694. else {
  695. CorruptedHeap(HEAP_CHECK_FAILURE, __FILE__, __LINE__);
  696. }
  697. pheap = (HEAP*) pheap->pNext;
  698. } while (pheap != NULL);
  699. wsprintf(pszReport,
  700. "Committed: %s\r\n%s Allocated: %s\r\n%s DeAllocated: %s\r\n",
  701. FormatNumber(curHeapAllocation),
  702. FormatNumber(cAllocated), FormatNumber(cbAllocated),
  703. FormatNumber(cDeAllocated), FormatNumber(cbDeAllocated));
  704. }
  705. /***************************************************************************
  706. FUNCTION: FormatNumber
  707. PURPOSE: Convert a number into a string, and insert commas every
  708. 3 digits
  709. PARAMETERS:
  710. num
  711. RETURNS: Pointer to the string containing the number
  712. COMMENTS:
  713. Cycles through an array of strings, allowing up to MAX_STRING
  714. requests before a duplicate would occur. This is important for
  715. calls to sprintf() where all the pointers are retrieved before
  716. the strings are actually used.
  717. MODIFICATION DATES:
  718. 03-Jul-1994 [ralphw]
  719. ***************************************************************************/
  720. #define MAX_NUM 15
  721. #define MAX_STRING 10
  722. #include <stdlib.h>
  723. PCSTR STDCALL FormatNumber(int num)
  724. {
  725. static int pos = 0;
  726. static char szNum[MAX_NUM * MAX_STRING];
  727. PSTR pszNum = szNum + (pos * MAX_STRING);
  728. if (++pos >= MAX_STRING)
  729. pos = 0;
  730. _itoa(num, pszNum, 10);
  731. int cb = strlen(pszNum) - 3;
  732. while (cb > 0) {
  733. memmove(pszNum + cb + 1, pszNum + cb, strlen(pszNum + cb) + 1);
  734. pszNum[cb] = ',';
  735. cb -= 3;
  736. }
  737. return pszNum;
  738. }