libcm is a C development framework with an emphasis on audio signal processing applications.
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.

cmLinkedHeap.c 9.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358
  1. #include "cmPrefix.h"
  2. #include "cmGlobal.h"
  3. #include "cmRpt.h"
  4. #include "cmErr.h"
  5. #include "cmCtx.h"
  6. #include "cmMem.h"
  7. #include "cmLinkedHeap.h"
  8. #include "cmMallocDebug.h"
  9. typedef struct cmLhBlock_str
  10. {
  11. char* basePtr; // base of block
  12. char* nextPtr; // next avail location in block
  13. char* endPtr; // one past end of block
  14. struct cmLhBlock_str* prevBlkPtr; // backward block link
  15. struct cmLhBlock_str* nextBlkPtr; // forward block link
  16. unsigned freeCnt; // track orphaned space that is unavailable for reuse
  17. } cmLhBlock_t;
  18. typedef struct
  19. {
  20. unsigned dfltBlockByteCnt; // size of each block in chain
  21. cmLhBlock_t* first; // first block in chain
  22. cmLhBlock_t* last; // last block in chain
  23. cmMmH_t mmH;
  24. } cmLHeap_t;
  25. cmLHeapH_t cmLHeapNullHandle = { NULL };
  26. cmLHeap_t* _cmLHeapHandleToPtr( cmLHeapH_t h )
  27. {
  28. cmLHeap_t* lhp = (cmLHeap_t*)h.h;
  29. assert( lhp != NULL);
  30. return lhp;
  31. }
  32. cmLhBlock_t* _cmLHeapAllocBlock( cmLHeap_t* lhp, unsigned blockByteCnt )
  33. {
  34. // note: the entire block (control record and data space) is allocated
  35. // as one contiguous chunk of memory.
  36. cmLhBlock_t* lbp = (cmLhBlock_t*)cmMemMallocZ( sizeof(cmLhBlock_t) + blockByteCnt );
  37. // setup the new block
  38. lbp->basePtr = (char*)(lbp+1);
  39. lbp->nextPtr = lbp->basePtr;
  40. lbp->endPtr = lbp->basePtr + blockByteCnt;
  41. lbp->prevBlkPtr = lhp->last;
  42. lbp->nextBlkPtr = NULL;
  43. // link the new block into the chain
  44. if( lhp->last != NULL )
  45. lhp->last->nextBlkPtr = lbp;
  46. else
  47. {
  48. assert( lhp->first == NULL );
  49. lhp->first = lbp;
  50. }
  51. lhp->last = lbp;
  52. return lbp;
  53. }
  54. void* _cmLHeapAlloc( cmLHeap_t* lhp, unsigned dataByteCnt )
  55. {
  56. void* retPtr = NULL;
  57. cmLhBlock_t* lbp = lhp->last;
  58. unsigned allocByteCnt = dataByteCnt + sizeof(unsigned);
  59. // go backwards down the chain looking for the first block with enough
  60. // free space to hold the allocation (we go backward under the assumption
  61. // that the last block is most likely to have available space)
  62. while( (lbp != NULL) && ((lbp->endPtr - lbp->nextPtr) < allocByteCnt) )
  63. lbp = lbp->prevBlkPtr;
  64. // no space large enough to provide the requested memory - allocate a new block
  65. if( lbp == NULL )
  66. lbp = _cmLHeapAllocBlock(lhp, cmMax( lhp->dfltBlockByteCnt, allocByteCnt ));
  67. assert( lbp != NULL );
  68. // store the sizeof the allocation at the beginning of the allocated block
  69. *(unsigned*)lbp->nextPtr = allocByteCnt;
  70. // the returned block ptr begins just after the block size
  71. retPtr = lbp->nextPtr + sizeof(unsigned);
  72. lbp->nextPtr += allocByteCnt;
  73. return retPtr;
  74. }
  75. void* _cmLHeapAllocCb(void* funcArgPtr, unsigned byteCnt)
  76. {
  77. cmLHeap_t* p = (cmLHeap_t*)funcArgPtr;
  78. assert( p != NULL );
  79. return _cmLHeapAlloc(p,byteCnt);
  80. }
  81. bool _cmLHeapFree( cmLHeap_t* lhp, void* dataPtr )
  82. {
  83. if( dataPtr == NULL )
  84. return true;
  85. cmLhBlock_t* lbp = lhp->first;
  86. // locate the block containing the area to free
  87. while( (lbp != NULL ) && (((char*)dataPtr < lbp->basePtr) || ((char*)dataPtr >= lbp->endPtr)))
  88. lbp = lbp->nextBlkPtr;
  89. // the pointer must be in one of the blocks
  90. if( lbp == NULL )
  91. return false;
  92. unsigned* allocPtr = ((unsigned*)dataPtr)-1;
  93. unsigned dataByteCnt = *allocPtr - sizeof(unsigned);
  94. // the data to be freed is at the end of the blocks space ...
  95. if( dataPtr == lbp->nextPtr - dataByteCnt )
  96. lbp->nextPtr = (char*)allocPtr; // ... then make it the space to alloc
  97. else
  98. lbp->freeCnt += *allocPtr; // ... otherwise increase the free count
  99. //(freeCnt tracks unused space that is not at the end of the block and therefore cannot be reused.)
  100. // if all the space for this block has been freed then the
  101. // next space to allocate must be at the base
  102. if( lbp->freeCnt == lbp->endPtr - lbp->basePtr )
  103. lbp->nextPtr = lbp->basePtr;
  104. return true;
  105. }
  106. bool _cmLHeapFreeCb(void* funcArgPtr, void* ptr)
  107. {
  108. cmLHeap_t* lhp = (cmLHeap_t*)funcArgPtr;
  109. return _cmLHeapFree(lhp,ptr);
  110. }
  111. cmLHeapH_t cmLHeapCreate( unsigned dfltBlockByteCnt, cmCtx_t* ctx )
  112. {
  113. cmLHeapH_t h;
  114. cmLHeap_t* lhp = cmMemAllocZ( cmLHeap_t, 1 );
  115. // We are not going to defer freeing each allocation because it will result in using
  116. // a lot of memory. Commenting out this line however would result in
  117. // checking all allocations for corruption during cmLHeapDestroy().
  118. // This may be a good way to hunt down subtle memory corruption problems.
  119. // See cmLHeapDestroy() and cmLHeapClear() for kDeferFreeMMFl related
  120. // items.
  121. unsigned mmFlags = cmClrFlag(ctx->mmFlags,kDeferFreeMmFl);
  122. if( cmMmInitialize(&lhp->mmH,_cmLHeapAllocCb,_cmLHeapFreeCb,lhp,ctx->guardByteCnt,ctx->alignByteCnt,mmFlags,&ctx->rpt) != kOkMmRC )
  123. return cmLHeapNullHandle;
  124. lhp->dfltBlockByteCnt = dfltBlockByteCnt;
  125. _cmLHeapAllocBlock( lhp, lhp->dfltBlockByteCnt );
  126. h.h = lhp;
  127. return h;
  128. }
  129. void cmLHeapDestroy( cmLHeapH_t* hp )
  130. {
  131. if( hp==NULL || hp->h == NULL )
  132. return;
  133. cmLHeap_t* lhp = _cmLHeapHandleToPtr(*hp);
  134. // check for corruption
  135. if( cmIsFlag(cmMmInitializeFlags(lhp->mmH),kDeferFreeMmFl))
  136. cmMmReport(lhp->mmH, kSuppressSummaryMmFl | kIgnoreLeaksMmFl | kIgnoreNormalMmFl );
  137. cmMmFinalize(&lhp->mmH);
  138. cmLhBlock_t* lbp = lhp->first;
  139. while( lbp != NULL )
  140. {
  141. cmLhBlock_t* t = lbp;
  142. lbp = lbp->nextBlkPtr;
  143. cmMemFree(t);
  144. }
  145. cmMemPtrFree(&hp->h);
  146. }
  147. void* cmLHeapAllocate(cmLHeapH_t h, void* orgDataPtr, unsigned eleCnt, unsigned eleByteCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine )
  148. { return cmMmAllocate(_cmLHeapHandleToPtr(h)->mmH,orgDataPtr,eleCnt,eleByteCnt,flags,fileStr,funcStr,fileLine); }
  149. cmChar_t* cmLHeapAllocStr(cmLHeapH_t h, void* orgDataPtr, const cmChar_t* str, unsigned charCnt, unsigned flags, const char* fileStr, const char* funcStr, unsigned fileLine )
  150. {
  151. if( str == NULL )
  152. return NULL;
  153. unsigned n = charCnt + 1;
  154. cmChar_t* cp = cmLHeapAllocate(h, orgDataPtr, n, sizeof(cmChar_t), flags, fileStr, funcStr, fileLine );
  155. strncpy(cp,str,n);
  156. cp[n-1] = 0;
  157. return cp;
  158. }
  159. void cmLHeapFree( cmLHeapH_t h, void* dataPtr )
  160. {
  161. cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
  162. cmMmFree(lhp->mmH,dataPtr);
  163. }
  164. void cmLHeapFreeDebug( cmLHeapH_t h, void* dataPtr, const char* fileName, const char* funcName, unsigned fileLine )
  165. {
  166. cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
  167. cmMmFreeDebug(lhp->mmH,dataPtr,fileName,funcName,fileLine);
  168. }
  169. void cmLHeapFreePtr( cmLHeapH_t h, void** ptrPtr )
  170. {
  171. assert( ptrPtr != NULL );
  172. cmLHeapFree(h,*ptrPtr);
  173. *ptrPtr = NULL;
  174. }
  175. void cmLHeapFreePtrDebug( cmLHeapH_t h, void** ptrPtr, const char* fileName, const char* funcName, unsigned fileLine )
  176. {
  177. assert( ptrPtr != NULL );
  178. cmLHeapFreeDebug(h,*ptrPtr,fileName,funcName,fileLine);
  179. *ptrPtr = NULL;
  180. }
  181. unsigned cmLHeapDefaultBlockByteCount( cmLHeapH_t h )
  182. {
  183. cmLHeap_t* p = _cmLHeapHandleToPtr(h);
  184. return p->dfltBlockByteCnt;
  185. }
  186. unsigned cmLHeapGuardByteCount( cmLHeapH_t h )
  187. {
  188. cmLHeap_t* p = _cmLHeapHandleToPtr(h);
  189. return cmMmGuardByteCount( p->mmH );
  190. }
  191. unsigned cmLHeapAlignByteCount( cmLHeapH_t h )
  192. {
  193. cmLHeap_t* p = _cmLHeapHandleToPtr(h);
  194. return cmMmAlignByteCount( p->mmH );
  195. }
  196. unsigned cmLHeapInitializeFlags( cmLHeapH_t h )
  197. {
  198. cmLHeap_t* p = _cmLHeapHandleToPtr(h);
  199. return cmMmInitializeFlags( p->mmH );
  200. }
  201. bool cmLHeapIsValid( cmLHeapH_t h )
  202. { return h.h != NULL; }
  203. void cmLHeapClear( cmLHeapH_t h, bool releaseFl )
  204. {
  205. cmLHeap_t* p = _cmLHeapHandleToPtr(h);
  206. // If we are deferring freeing memory until the heap is destroyed
  207. // then we cannot clear the block list in this function.
  208. // If the block list was cleared here then later calls to _cmLHeapFreeCb()
  209. // would fail because the pointers to the deferred allocations
  210. // would no longer exist in any of the blocks.
  211. if( cmIsFlag(cmMmInitializeFlags(p->mmH),kDeferFreeMmFl))
  212. return;
  213. cmLhBlock_t* bp = p->first;
  214. while( bp != NULL )
  215. {
  216. bp->nextPtr = bp->basePtr;
  217. bp->freeCnt = bp->endPtr - bp->basePtr;
  218. cmLhBlock_t* nbp = bp->nextBlkPtr;
  219. if( releaseFl )
  220. cmMemFree(bp);
  221. bp = nbp;
  222. }
  223. if( releaseFl )
  224. {
  225. p->first = NULL;
  226. p->last = NULL;
  227. }
  228. }
  229. cmMmRC_t cmLHeapReportErrors( cmLHeapH_t h, unsigned mmFlags )
  230. {
  231. cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
  232. return cmMmReport(lhp->mmH, mmFlags );
  233. }
  234. void cmLHeapReport( cmLHeapH_t h )
  235. {
  236. cmLHeap_t* lhp = _cmLHeapHandleToPtr(h);
  237. cmLhBlock_t* lbp = lhp->first;
  238. unsigned i;
  239. for(i=0; lbp != NULL; ++i )
  240. {
  241. printf("%u : %li available %i free\n", i, lbp->endPtr-lbp->nextPtr, lbp->freeCnt );
  242. lbp = lbp->nextBlkPtr;
  243. }
  244. printf("\n");
  245. }
  246. void cmLHeapTestPrint( void* userPtr, const char* text )
  247. {
  248. fputs(text,stdin);
  249. }
  250. void cmLHeapTest()
  251. {
  252. int i = 0;
  253. int n = 5;
  254. unsigned guardByteCnt = 8;
  255. unsigned alignByteCnt = 16;
  256. unsigned mmFlags = kTrackMmFl; // | kDeferFreeMmFl;
  257. cmCtx_t ctx;
  258. cmCtxSetup(&ctx,"Heap Test",cmLHeapTestPrint,cmLHeapTestPrint,NULL,guardByteCnt,alignByteCnt,mmFlags);
  259. cmLHeapH_t h = cmLHeapCreate( 16, &ctx );
  260. void* pArray[ n ];
  261. cmLHeapReport(h);
  262. for(i=0; i<n; ++i)
  263. {
  264. unsigned byteCnt = (i+1) * 2;
  265. printf("Allocating:%li\n",byteCnt+sizeof(unsigned));
  266. pArray[i] = cmLHeapAlloc(h,byteCnt);
  267. cmLHeapReport(h);
  268. }
  269. for(i=n-1; i>=0; i-=2)
  270. {
  271. printf("Freeing:%li\n",((i+1) * 2) + sizeof(unsigned));
  272. cmLHeapFree(h,pArray[i]);
  273. cmLHeapReport(h);
  274. }
  275. cmLHeapReportErrors(h,0);
  276. cmLHeapDestroy(&h);
  277. }