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 10KB

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