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.

661 lines
23 KiB

  1. /*************************************************************************
  2. * *
  3. * BLOCKMGR.C *
  4. * *
  5. * Copyright (C) Microsoft Corporation 1990-1994 *
  6. * All Rights reserved. *
  7. * *
  8. **************************************************************************
  9. * *
  10. * Module Intent *
  11. * Memory block management. *
  12. * Consider the case of creating a string table. *
  13. * 1/ First we have to allocate a block of memory *
  14. * 2/ Then we copy all the strings into the block, until we run out *
  15. * space. *
  16. * 3/ Either we will increase the size of the memory block (using *
  17. * _frealloc(), which requires the block's size < 64K, or _halloc *
  18. * which require huge pointers, or we allocate another block of *
  19. * memory, and connected the 2nd block to the 1st block *
  20. * *
  21. * The purpose of this module is to providesimple interface when *
  22. * handling memory block in the second scenario. *
  23. * *
  24. * The block manager will allocate one initiale memory block, and as *
  25. * memory need arises, more block of memory are allocated transparently *
  26. * An example of how to use the memory block manager would be: *
  27. * *
  28. * lpBlock = BlockInitiate (BlockSize, wElemSize); *
  29. * for (i = 0; condition; i++) { *
  30. * if ((Array[i] = BlockCopy (lpBlock, Buffer, BufLen)) *
  31. * == NULL) *
  32. * Error(); *
  33. * } *
  34. * *
  35. * Advantages: *
  36. * - Caller doesn't have to worry about how much room is left *
  37. * - We can use the maximum memory space if needed to *
  38. * - We don't have to use huge pointers *
  39. * *
  40. * Comments: *
  41. * This scheme doesn't assume how memory are used/referenced. To *
  42. * satisfy all the needs, the memory blocks have to be locked *
  43. * permanently. This may cause OOM problems when the memory is *
  44. * extremely fragmented, and garbage collection is hampered by not *
  45. * being able to move the block around. The problem is minimized if *
  46. * the block's size is large (ie. minimize fragmentation), which is *
  47. * usually the case *
  48. * Anyway, loking and unlocking problem should go away on 32-bit *
  49. * and above system *
  50. **************************************************************************
  51. * *
  52. * Written By : Binh Nguyen *
  53. * Current Owner: Binh Nguyen *
  54. * *
  55. **************************************************************************/
  56. #include <mvopsys.h>
  57. #include <misc.h>
  58. #include <memory.h> // For _fmemcpy
  59. #include <mem.h>
  60. #include <_mvutil.h>
  61. #ifdef _DEBUG
  62. static BYTE s_aszModule[] = __FILE__; // Used by error return functions.
  63. #endif
  64. /* Stamp to do some limited validation checking */
  65. #define BLOCK_STAMP 1234
  66. /*************************************************************************
  67. *
  68. * INTERNAL PRIVATE FUNCTIONS
  69. * All of them should be declared near
  70. *************************************************************************/
  71. PRIVATE int PASCAL NEAR BlockInitialize (LPBLK, BLOCK FAR *);
  72. /*************************************************************************
  73. *
  74. * INTERNAL PUBLIC FUNCTIONS
  75. * All of them should be declared far, and included in some include file
  76. *************************************************************************/
  77. PUBLIC LPB PASCAL FAR BlockReset (LPV);
  78. PUBLIC VOID PASCAL FAR BlockFree (LPV);
  79. PUBLIC LPV PASCAL FAR BlockInitiate (DWORD, WORD, WORD, int);
  80. PUBLIC LPV PASCAL FAR BlockCopy (LPV, LPB, DWORD, WORD);
  81. PUBLIC LPV PASCAL FAR BlockGetElement(LPV);
  82. PUBLIC int PASCAL FAR BlockGrowth (LPV);
  83. PUBLIC LPB PASCAL FAR BlockGetLinkedList(LPV);
  84. PUBLIC LPV PASCAL FAR GlobalLockedStructMemAlloc (DWORD);
  85. PUBLIC VOID PASCAL FAR GlobalLockedStructMemFree (HANDLE FAR *);
  86. /*************************************************************************
  87. * @doc INTERNAL RETRIEVAL
  88. *
  89. * @func VOID PASCAL NEAR | BlockThreadElement |
  90. * This function will thread all the elements of a memory block
  91. * into a linked list
  92. *
  93. * @parm LPB | lpbBuf |
  94. * Pointer to memory buffer
  95. *
  96. * @parm DWORD | BufSize |
  97. * Total buffer's size
  98. *
  99. * @parm WORD | wElemSize |
  100. * Element's size
  101. *************************************************************************/
  102. PRIVATE VOID PASCAL NEAR BlockThreadElement (LPB lpbBuf, DWORD BufSize,
  103. WORD wElemSize)
  104. {
  105. register DWORD cElem;
  106. if (wElemSize == 0)
  107. return;
  108. for (cElem = BufSize / wElemSize; cElem > 1; cElem --)
  109. {
  110. *(LPB UNALIGNED *UNALIGNED)lpbBuf = lpbBuf + wElemSize;
  111. lpbBuf += wElemSize;
  112. }
  113. *(LPB UNALIGNED *UNALIGNED)lpbBuf = NULL;
  114. }
  115. /*************************************************************************
  116. * @doc INTERNAL RETRIEVAL
  117. *
  118. * @func int PASCAL FAR | BlockGrowth |
  119. * Create another memory block and link it into the block list
  120. *
  121. * @parm LPBLK | lpBlockHead |
  122. * Pointer to block manager node
  123. *
  124. * @rdesc S_OK, or E_OUTOFMEMORY, or E_INVALIDARG
  125. *
  126. * @comm This function should be called externally only in the case
  127. * of running out of threaded data.
  128. *************************************************************************/
  129. PUBLIC int PASCAL FAR BlockGrowth (LPBLK lpBlockHead)
  130. {
  131. BLOCK FAR *lpBlock;
  132. DWORD BlockSize;
  133. if (lpBlockHead == NULL)
  134. return E_INVALIDARG;
  135. BlockSize = lpBlockHead->cBytePerBlock;
  136. /* Check to see if we already allocate the block. This happens
  137. * after calling BlockReset(), all the blocks are still there
  138. * unused and linked together
  139. */
  140. if (lpBlockHead->lpCur->lpNext == NULL)
  141. {
  142. /* Ensure that we did not pass the limit number of blocks allowed */
  143. if (lpBlockHead->cCurBlockCnt >= lpBlockHead->cMaxBlock)
  144. return E_OUTOFMEMORY;
  145. lpBlockHead->cCurBlockCnt ++;
  146. if (lpBlockHead->fFlag & USE_VIRTUAL_MEMORY)
  147. {
  148. #ifndef _MAC // {_MAC
  149. DWORD size = (DWORD)(BlockSize + sizeof(BLOCK));
  150. if ((lpBlock = _VIRTUALALLOC(NULL, size, MEM_COMMIT,
  151. PAGE_READWRITE)) == NULL)
  152. {
  153. return E_OUTOFMEMORY;
  154. }
  155. _VIRTUALLOCK(lpBlock, size);
  156. #endif // } _MAC
  157. }
  158. else
  159. {
  160. /* Allocate a new block */
  161. if ((lpBlock = GlobalLockedStructMemAlloc
  162. ((DWORD)(BlockSize + sizeof(BLOCK)))) == NULL)
  163. {
  164. return E_OUTOFMEMORY;
  165. }
  166. }
  167. lpBlock->wStamp = BLOCK_STAMP;
  168. }
  169. else
  170. lpBlock = lpBlockHead->lpCur->lpNext;
  171. /* Link the block into the list */
  172. lpBlockHead->lpCur->lpNext = lpBlock;
  173. if (lpBlockHead->fFlag & THREADED_ELEMENT)
  174. {
  175. BlockThreadElement ((LPB)lpBlock + sizeof(BLOCK),
  176. BlockSize, lpBlockHead->wElemSize);
  177. }
  178. /* Update all information */
  179. return BlockInitialize (lpBlockHead, lpBlock);
  180. }
  181. /*************************************************************************
  182. * @doc INTERNAL RETRIEVAL
  183. *
  184. * @func int PASCAL NEAR | BlockInitialize |
  185. * Iniitalize the fields of the block manager structure
  186. *
  187. * @parm LPBLK | lpBlockHead |
  188. * Pointer to the block manager structure
  189. *
  190. * @parm BLOCK FAR * | lpBlock |
  191. * Pointer to the block of memory
  192. *
  193. * @rdesc S_OK if succeeded, else other errors
  194. *************************************************************************/
  195. PRIVATE int PASCAL NEAR BlockInitialize (LPBLK lpBlockHead, BLOCK FAR *lpBlock)
  196. {
  197. /* Validity check */
  198. if (lpBlockHead == NULL || lpBlock == NULL)
  199. return E_INVALIDARG;
  200. /* Update all information */
  201. lpBlockHead->lpCur = lpBlock;
  202. lpBlockHead->lpbCurLoc = (LPB)lpBlock + sizeof(BLOCK);
  203. lpBlockHead->lTotalSize += lpBlockHead->cBytePerBlock + sizeof(BLOCK);
  204. /* If the memory block is threaded, then set cByteLeft = 0. This
  205. * to ensure that whoever use threaded blocks have to manage their
  206. * own linked list blocks, and ensure that calls to BlockGetElement()
  207. * will ultimately fail
  208. */
  209. lpBlockHead->cByteLeft = (lpBlockHead->fFlag & THREADED_ELEMENT) ?
  210. 0 : lpBlockHead->cBytePerBlock;
  211. return S_OK;
  212. }
  213. /*************************************************************************
  214. * @doc INTERNAL RETRIEVAL
  215. *
  216. * @func LPB PASCAL FAR | BlockReset |
  217. * Reset the block manager, ie. just start from beginning without
  218. * releasing the memory blocks
  219. *
  220. * @parm LPBLK | lpBlockHead |
  221. * Pointer to the block manager structure
  222. *
  223. * @rdesc Pointer to start of the buffer, or NULL if errors. This is to
  224. * ensure that if the block is threaded, we returned the pointer of
  225. * the first element in the list
  226. *************************************************************************/
  227. PUBLIC LPB PASCAL FAR BlockReset (LPBLK lpBlockHead)
  228. {
  229. /* Check for block validity */
  230. if (lpBlockHead == NULL || lpBlockHead->wStamp != BLOCK_STAMP)
  231. return NULL;
  232. /* Update all information, start from the beginning of the list */
  233. lpBlockHead->lpCur = lpBlockHead->lpHead;
  234. lpBlockHead->lpbCurLoc = (LPB)lpBlockHead->lpCur + sizeof(BLOCK);
  235. lpBlockHead->lTotalSize = lpBlockHead->cBytePerBlock + sizeof(BLOCK);
  236. /* Do the threading if necessary */
  237. if ((lpBlockHead->fFlag & THREADED_ELEMENT))
  238. {
  239. BlockThreadElement(lpBlockHead->lpbCurLoc, lpBlockHead->cBytePerBlock,
  240. lpBlockHead->wElemSize);
  241. /* Ensure that BlockGetElement() will fail, ie. the user must
  242. * handle the linked list of elements himself.
  243. */
  244. lpBlockHead->cByteLeft = 0;
  245. }
  246. else
  247. lpBlockHead->cByteLeft = lpBlockHead->cBytePerBlock;
  248. return lpBlockHead->lpbCurLoc;
  249. }
  250. /*************************************************************************
  251. * @doc INTERNAL RETRIEVAL
  252. *
  253. * @func VOID PASCAL FAR | BlockFree |
  254. * Free the block manager strucutre and all the memory blocks
  255. * associated with it
  256. *
  257. * @parm LPBLK | lpBlockHead |
  258. * Pointer to block manager structure
  259. *************************************************************************/
  260. PUBLIC VOID PASCAL FAR BlockFree (LPBLK lpBlockHead)
  261. {
  262. BLOCK FAR *lpBlock;
  263. BLOCK FAR *lpNextBlock;
  264. /* Check for block validity */
  265. if (lpBlockHead == NULL || lpBlockHead->wStamp != BLOCK_STAMP)
  266. return;
  267. /* Free all memory block associated with the block manager */
  268. for (lpBlock = lpBlockHead->lpHead; lpBlock; lpBlock = lpNextBlock)
  269. {
  270. lpNextBlock = lpBlock->lpNext;
  271. /* Only free the block if it is valid */
  272. if (lpBlock->wStamp == BLOCK_STAMP)
  273. {
  274. if (lpBlockHead->fFlag & USE_VIRTUAL_MEMORY)
  275. {
  276. #ifndef _MAC // { _MAC
  277. _VIRTUALUNLOCK (lpBlock, lpBlockHead->cBytePerBlock +
  278. sizeof(BLOCK));
  279. _VIRTUALFREE (lpBlock, 0L, (MEM_DECOMMIT | MEM_RELEASE));
  280. #endif // } _MAC
  281. }
  282. else
  283. GlobalLockedStructMemFree ((LPV)lpBlock);
  284. }
  285. }
  286. /* Free the block manager */
  287. GlobalLockedStructMemFree((LPV)lpBlockHead);
  288. }
  289. /*************************************************************************
  290. * @doc INTERNAL RETRIEVAL
  291. *
  292. * @func LPV PASCAL FAR | BlockInitiate |
  293. * Initiate and allocate memory block for block management
  294. *
  295. * @parm DWORD | BlockSize |
  296. * Block's size. The block size should less than 0xffff - 16 -
  297. * sizeof(BLOCK) for 16-bit build
  298. *
  299. * @parm WORD | wElemSize |
  300. * Size of an element, useful for fixed size data structure
  301. *
  302. * @parm WORD | cMaxBlock
  303. * Maximum number of blocks that can be allocated. 0 means 64K
  304. *
  305. * @parm int | flag |
  306. * - THREADED_ELEMENT : if the elements are to be threaded together into a
  307. * linked list
  308. * - USE_VIRTUAL_MEMORY : if virtual memory is to be used
  309. *
  310. * @rdesc Return pointer to a block management structure, or NULL
  311. * if OOM
  312. *************************************************************************/
  313. PUBLIC LPV PASCAL FAR BlockInitiate (DWORD BlockSize, WORD wElemSize,
  314. WORD cMaxBlock, int flag)
  315. {
  316. LPBLK lpBlockHead;
  317. BLOCK FAR *lpBlock;
  318. /* Check for valid size. We add
  319. * - sizeof(BLOCK)
  320. * - 16 bytes to ensure that we never cross the 64K limit boundary
  321. */
  322. #ifndef _32BIT
  323. if (BlockSize >= (unsigned)0xffff - sizeof(BLOCK) - 16)
  324. return NULL;
  325. #endif
  326. /* Allocate a block head. All fields are zero's except when
  327. * initialized
  328. */
  329. if ((lpBlockHead = GlobalLockedStructMemAlloc(sizeof(BLOCK_MGR))) == NULL)
  330. return NULL;
  331. /* Allocate a memory block */
  332. if (flag & USE_VIRTUAL_MEMORY)
  333. {
  334. #ifndef _MAC // { _MAC
  335. DWORD size = (DWORD)(BlockSize + sizeof(BLOCK));
  336. if ((lpBlockHead->lpHead = lpBlock =
  337. _VIRTUALALLOC(NULL, size, MEM_COMMIT, PAGE_READWRITE)) == NULL)
  338. {
  339. GlobalLockedStructMemFree((LPV)lpBlockHead);
  340. return NULL;
  341. }
  342. if (_VIRTUALLOCK(lpBlock, size) == 0)
  343. GetLastError();
  344. #endif // } _MAC
  345. }
  346. else
  347. {
  348. if ((lpBlockHead->lpHead = lpBlock =
  349. GlobalLockedStructMemAlloc((DWORD)(BlockSize +
  350. sizeof(BLOCK)))) == NULL)
  351. {
  352. GlobalLockedStructMemFree((LPV)lpBlockHead);
  353. return NULL;
  354. }
  355. }
  356. lpBlock->wStamp = BLOCK_STAMP;
  357. /* Initialization block manager structure */
  358. lpBlockHead->wStamp = BLOCK_STAMP;
  359. lpBlockHead->lpCur = lpBlock;
  360. lpBlockHead->cByteLeft = lpBlockHead->cBytePerBlock = BlockSize;
  361. lpBlockHead->lpbCurLoc = (LPB)lpBlock + sizeof(BLOCK);
  362. lpBlockHead->wElemSize = wElemSize;
  363. lpBlockHead->lTotalSize = BlockSize + sizeof(BLOCK);
  364. if (cMaxBlock == 0)
  365. lpBlockHead->cMaxBlock = 0xffff;
  366. else
  367. lpBlockHead->cMaxBlock = cMaxBlock;
  368. lpBlockHead->cCurBlockCnt = 1; /* We have 1 block in the list */
  369. if ((lpBlockHead->fFlag = (WORD)flag) & THREADED_ELEMENT)
  370. {
  371. BlockThreadElement (lpBlockHead->lpbCurLoc, BlockSize, wElemSize);
  372. }
  373. return lpBlockHead;
  374. }
  375. /*************************************************************************
  376. * @doc INTERNAL RETRIEVAL
  377. *
  378. * @func LPV PASCAL FAR | BlockCopy |
  379. * Copy a buffer into the memory block. The function will allocate
  380. * more memory if needed
  381. *
  382. * @parm LPBLK | lpBlockHead |
  383. * Pointer to manager block
  384. *
  385. * @parm LPB | Buffer |
  386. * Buffer to be copied: if this is NULL, the alloc is done, but
  387. * no copy is performed.
  388. *
  389. * @parm DWORD | BufSize |
  390. * Size of the buffer
  391. *
  392. * @parm WORD | wStartOffset |
  393. * Offset of the start of the buffer. Extra memory is needed to
  394. * accomodate the offset. This is needed because we use have
  395. * {structure+buffer} structure. The starting offset would be
  396. * the size of the structure
  397. *
  398. * @rdesc
  399. * Return pointer to the {structure+buffer} memory block, or NULL
  400. * if OOM or bad argument
  401. *************************************************************************/
  402. PUBLIC LPV PASCAL FAR BlockCopy (LPBLK lpBlockHead, LPB Buffer,
  403. DWORD BufSize, WORD wStartOffset)
  404. {
  405. LPB lpbRetBuf;
  406. DWORD wTotalLength = BufSize + wStartOffset;
  407. #ifdef _DEBUG
  408. if (lpBlockHead == NULL || lpBlockHead->wStamp != BLOCK_STAMP ||
  409. BufSize == 0)
  410. return NULL;
  411. #endif
  412. // Block 4-byte alignement
  413. wTotalLength = (wTotalLength + 3) & (~3);
  414. /* Check for room */
  415. if (wTotalLength > lpBlockHead->cByteLeft)
  416. {
  417. if (BlockGrowth (lpBlockHead) != S_OK ||
  418. (wTotalLength > lpBlockHead->cByteLeft))
  419. return NULL;
  420. }
  421. lpbRetBuf = lpBlockHead->lpbCurLoc;
  422. /* Do the copy */
  423. if (Buffer)
  424. MEMCPY (lpbRetBuf + wStartOffset, Buffer, BufSize);
  425. /* Update the pointer and the number of bytes left */
  426. lpBlockHead->lpbCurLoc += wTotalLength;
  427. lpBlockHead->cByteLeft -= wTotalLength;
  428. return (LPV)lpbRetBuf;
  429. }
  430. /*************************************************************************
  431. * @doc INTERNAL RETRIEVAL
  432. *
  433. * @func LPB PASCAL FAR | BlockGetLinkedList |
  434. * Return the pointer to the linked list of the element
  435. *
  436. * @parm LPBLK | lpBlockHead |
  437. * Pointer to block manager structure
  438. *
  439. * @rdesc NULL if bad argument, else pointer to the linked list. One
  440. * possible bad argument is that the block is not marked as threaded
  441. * in BlockInitiate()
  442. *************************************************************************/
  443. PUBLIC LPB PASCAL FAR BlockGetLinkedList(LPBLK lpBlockHead)
  444. {
  445. if (lpBlockHead == NULL || lpBlockHead->wStamp != BLOCK_STAMP ||
  446. (lpBlockHead->fFlag & THREADED_ELEMENT) == 0)
  447. return NULL;
  448. return lpBlockHead->lpbCurLoc;
  449. }
  450. /*************************************************************************
  451. * @doc INTERNAL RETRIEVAL
  452. *
  453. * @func LPV PASCAL FAR | BlockGetElement |
  454. * The function returns the pointer to the current element in the
  455. * buffer
  456. *
  457. * @parm LPBLK | lpBlockHead |
  458. * Pointer to block manager structure
  459. *
  460. * @rdesc pointer to current element, or NULL if OOM
  461. *
  462. * @comm After the call, all offsets/pointers are updated preparing
  463. * for the next call
  464. *************************************************************************/
  465. PUBLIC LPV PASCAL FAR BlockGetElement(LPBLK lpBlockHead)
  466. {
  467. LPB lpbRetBuf;
  468. WORD wElemSize;
  469. #ifdef _DEBUG
  470. if (lpBlockHead == NULL || lpBlockHead->wStamp != BLOCK_STAMP)
  471. return NULL;
  472. #endif
  473. if ((wElemSize = lpBlockHead->wElemSize) == 0)
  474. return NULL;
  475. /* Check for room */
  476. if (wElemSize > lpBlockHead->cByteLeft)
  477. {
  478. if ((BlockGrowth (lpBlockHead) != S_OK) ||
  479. wElemSize > lpBlockHead->cByteLeft)
  480. return NULL;
  481. }
  482. /* Get the returned pointer */
  483. lpbRetBuf = lpBlockHead->lpbCurLoc;
  484. /* Update the current pointer and the number of bytes left */
  485. lpBlockHead->lpbCurLoc += wElemSize;
  486. lpBlockHead->cByteLeft -= wElemSize;
  487. return (LPV)lpbRetBuf;
  488. }
  489. /*************************************************************************
  490. * @doc INTERNAL INDEX RETRIEVAL
  491. *
  492. * @func LPV PASCAL FAR | GlobalLockedStructMemAlloc |
  493. * This function allocates and return a pointer to a block of
  494. * memory. The first element of the structure must be the handle
  495. * to this block of memory
  496. *
  497. * @parm WORD | size |
  498. * Size of the structure block.
  499. *
  500. * @rdesc NULL if OOM, or pointer to the structure
  501. *************************************************************************/
  502. PUBLIC LPV PASCAL FAR GlobalLockedStructMemAlloc (DWORD size)
  503. {
  504. HANDLE hMem;
  505. HANDLE FAR *lpMem;
  506. if ((hMem = _GLOBALALLOC(DLLGMEM_ZEROINIT, (DWORD)size)) == 0)
  507. return NULL;
  508. lpMem = (HANDLE FAR *)_GLOBALLOCK(hMem);
  509. *lpMem = hMem;
  510. return (LPV)lpMem;
  511. }
  512. /*************************************************************************
  513. * @doc INTERNAL INDEX RETRIEVAL
  514. *
  515. * @func LPV PASCAL FAR | GlobalLockedStructMemFree |
  516. * This function free the block of memory pointed by lpMem. The
  517. * assumption here is that the 1st field of the block is the
  518. * handle to the block of memory.
  519. *
  520. * @parm WORD FAR * | lpMem |
  521. * Pointer to the block of memory to be freed
  522. *************************************************************************/
  523. PUBLIC VOID PASCAL FAR GlobalLockedStructMemFree (HANDLE FAR *lpMem)
  524. {
  525. HANDLE hMem;
  526. if (lpMem == NULL || (hMem = (HANDLE)*lpMem) == 0)
  527. return;
  528. _GLOBALUNLOCK(hMem);
  529. _GLOBALFREE(hMem);
  530. }
  531. /*************************************************************************
  532. * @doc INTERNAL RETRIEVAL
  533. *
  534. * @func int PASCAL FAR | BlockGetOrdinalBlock |
  535. * Retrieve pointer to the start of i-th block that was allocated (starting at zero)
  536. *
  537. * @parm LPBLK | lpBlockHead |
  538. * Pointer to block manager node
  539. *
  540. * @rdesc Pointer to first elt in block if successful, NULL otherwise
  541. *
  542. * @comm
  543. * This is used to get fast random access to the i-th elt in
  544. * an append-only linked list.
  545. *************************************************************************/
  546. PUBLIC LPB PASCAL FAR BlockGetOrdinalBlock (LPBLK lpBlockHead, WORD iBlock)
  547. {
  548. LPBLOCK lpBlock;
  549. if (lpBlockHead == NULL || lpBlockHead->wStamp != BLOCK_STAMP)
  550. return NULL;
  551. for (lpBlock = lpBlockHead->lpHead; iBlock && lpBlock; lpBlock = lpBlock->lpNext, iBlock--)
  552. ;
  553. return ((LPB)lpBlock + sizeof(BLOCK));
  554. }
  555. PUBLIC LPVOID PASCAL FAR BlockGetBlock (LPBLK lpBlockHead, DWORD dwSize)
  556. {
  557. LPB lpbRetBuf;
  558. #ifdef _DEBUG
  559. if (lpBlockHead == NULL || lpBlockHead->wStamp != BLOCK_STAMP)
  560. return NULL;
  561. #endif
  562. // 4-byte alignment
  563. dwSize = (dwSize + 3) & (~3);
  564. /* Check for room */
  565. if (dwSize > lpBlockHead->cByteLeft)
  566. {
  567. if ((BlockGrowth (lpBlockHead) != S_OK) ||
  568. dwSize > lpBlockHead->cByteLeft)
  569. return NULL;
  570. }
  571. /* Get the returned pointer */
  572. lpbRetBuf = lpBlockHead->lpbCurLoc;
  573. /* Update the current pointer and the number of bytes left */
  574. lpBlockHead->lpbCurLoc += dwSize;
  575. lpBlockHead->cByteLeft -= dwSize;
  576. return (LPV)lpbRetBuf;
  577. }
  578. VOID PASCAL FAR SetBlockCount (LPBLK lpBlock, WORD count)
  579. {
  580. lpBlock->cMaxBlock = count;
  581. }