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.

cmStack.c 9.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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 "cmMallocDebug.h"
  10. #include "cmStack.h"
  11. typedef struct cmStkBlk_str
  12. {
  13. char* base;
  14. unsigned curEleCnt;
  15. unsigned maxEleCnt;
  16. struct cmStkBlk_str* link;
  17. } cmStkBlk_t;
  18. typedef struct
  19. {
  20. cmErr_t err;
  21. unsigned expandBlkEleCnt; // count of ele's in all blocks after the first block
  22. unsigned cnt; // cur total count of elements in all blocks
  23. cmStkBlk_t* baseBlkPtr; // first block
  24. cmStkBlk_t* curBlkPtr; // current block - all blocks are always full except for the current block
  25. unsigned eleByteCnt; // bytes per element
  26. } cmStack_t;
  27. cmStackH_t cmStackNullHandle = cmSTATIC_NULL_HANDLE;
  28. cmStack_t* _cmStHandleToPtr( cmStackH_t h )
  29. {
  30. cmStack_t* p = (cmStack_t*)h.h;
  31. assert( p != NULL );
  32. return p;
  33. }
  34. cmStkBlk_t* _cmStkAllocBlk( cmStack_t* p, unsigned blkEleCnt )
  35. {
  36. cmStkBlk_t* blkPtr;
  37. // if a next block has already been created (because an earlier stack was cleared (w/o release)
  38. if( p->curBlkPtr != NULL && p->curBlkPtr->link != NULL )
  39. blkPtr = p->curBlkPtr->link;
  40. else
  41. {
  42. // ... otherwise allocate a new next block
  43. char* cp = cmMemAllocZ(char,sizeof(cmStkBlk_t) + p->eleByteCnt * blkEleCnt );
  44. blkPtr = (cmStkBlk_t*)cp;
  45. blkPtr->base = cp + sizeof(cmStkBlk_t);
  46. blkPtr->curEleCnt = 0;
  47. blkPtr->maxEleCnt = blkEleCnt;
  48. if( p->curBlkPtr != NULL )
  49. p->curBlkPtr->link = blkPtr;
  50. }
  51. if( p->baseBlkPtr == NULL )
  52. p->baseBlkPtr = blkPtr;
  53. p->curBlkPtr = blkPtr;
  54. return blkPtr;
  55. }
  56. cmStkBlk_t* _cmStackClear( cmStack_t* p, bool releaseFl )
  57. {
  58. cmStkBlk_t* bp = p->baseBlkPtr;
  59. while( bp != NULL )
  60. {
  61. cmStkBlk_t* nbp = bp->link;
  62. bp->curEleCnt = 0;
  63. if( releaseFl )
  64. cmMemFree(bp);
  65. bp = nbp;
  66. }
  67. p->cnt = 0;
  68. if( releaseFl )
  69. {
  70. p->curBlkPtr = NULL;
  71. p->baseBlkPtr = NULL;
  72. }
  73. else
  74. {
  75. p->curBlkPtr = p->baseBlkPtr;
  76. }
  77. return bp;
  78. }
  79. cmStRC_t _cmStackFree( cmStack_t* p )
  80. {
  81. cmStRC_t rc = kOkStRC;
  82. _cmStackClear(p,true);
  83. cmMemFree(p);
  84. return rc;
  85. }
  86. cmStRC_t cmStackAlloc( cmCtx_t* ctx, cmStackH_t* hp, unsigned initCnt, unsigned expandCnt, unsigned eleByteCnt )
  87. {
  88. cmStRC_t rc = kOkStRC;
  89. if((rc = cmStackFree(hp)) != kOkStRC )
  90. return rc;
  91. cmStack_t* p = cmMemAllocZ(cmStack_t,1);
  92. cmErrSetup(&p->err,&ctx->rpt,"Stack");
  93. p->expandBlkEleCnt = expandCnt;
  94. p->eleByteCnt = eleByteCnt;
  95. _cmStkAllocBlk(p,initCnt); // allocate the initial block
  96. hp->h = p;
  97. return rc;
  98. }
  99. cmStRC_t cmStackFree( cmStackH_t* hp )
  100. {
  101. cmStRC_t rc = kOkStRC;
  102. if( hp==NULL || cmStackIsValid(*hp) == false )
  103. return kOkStRC;
  104. cmStack_t* p = _cmStHandleToPtr(*hp);
  105. if((rc = _cmStackFree(p)) != kOkStRC )
  106. goto errLabel;
  107. hp->h = NULL;
  108. errLabel:
  109. return rc;
  110. }
  111. cmStRC_t cmStackIsValid( cmStackH_t h )
  112. { return h.h != NULL; }
  113. unsigned cmStackCount( cmStackH_t h )
  114. {
  115. cmStack_t* p = _cmStHandleToPtr(h);
  116. return p->cnt;
  117. }
  118. void cmStackClear( cmStackH_t h, bool releaseFl )
  119. {
  120. if( cmStackIsValid(h) == false )
  121. return;
  122. cmStack_t* p = _cmStHandleToPtr(h);
  123. _cmStackClear(p,releaseFl);
  124. }
  125. cmStRC_t cmStackPush( cmStackH_t h, const void* data, unsigned dataEleCnt )
  126. {
  127. cmStRC_t rc = kOkStRC;
  128. cmStack_t* p = _cmStHandleToPtr(h);
  129. while( dataEleCnt )
  130. {
  131. if( p->curBlkPtr == NULL || p->curBlkPtr->curEleCnt >= p->curBlkPtr->maxEleCnt )
  132. _cmStkAllocBlk(p, p->expandBlkEleCnt );
  133. // calc the count of ele's to copy into this block
  134. unsigned en = cmMin(dataEleCnt, p->curBlkPtr->maxEleCnt - p->curBlkPtr->curEleCnt );
  135. // calc the count of source bytes to copy into this block
  136. unsigned bn = en * p->eleByteCnt;
  137. // copy the source bytes into the block
  138. memcpy( p->curBlkPtr->base + (p->curBlkPtr->curEleCnt * p->eleByteCnt), data, bn );
  139. p->curBlkPtr->curEleCnt += en; // incr block element count
  140. p->cnt += en; // incr the total element count
  141. data = ((char*)data) + bn; // incr the source pointer
  142. dataEleCnt -= en; // decr the source ele count
  143. }
  144. return rc;
  145. }
  146. // Return the block and local index of the element at index *idxPtr.
  147. cmStkBlk_t* _cmStBlockIndex( cmStack_t* p, unsigned* idxPtr )
  148. {
  149. cmStkBlk_t* blkPtr;
  150. assert( p->baseBlkPtr != NULL);
  151. if( *idxPtr < p->baseBlkPtr->maxEleCnt )
  152. return p->baseBlkPtr;
  153. *idxPtr -= p->baseBlkPtr->maxEleCnt;
  154. unsigned bi = *idxPtr / p->expandBlkEleCnt;
  155. unsigned i;
  156. blkPtr = p->baseBlkPtr->link;
  157. for(i=0; i<bi; ++i)
  158. blkPtr = blkPtr->link;
  159. *idxPtr = *idxPtr - bi * p->expandBlkEleCnt;
  160. return blkPtr;
  161. }
  162. cmStRC_t cmStackPop( cmStackH_t h, unsigned eleCnt )
  163. {
  164. cmStRC_t rc = kOkStRC;
  165. cmStack_t* p = _cmStHandleToPtr(h);
  166. if( p->cnt < eleCnt )
  167. return cmErrMsg(&p->err,kUnderflowStRC,"Stack underflow. Cannot pop %i elements off stack with %i elements.", eleCnt, p->cnt );
  168. unsigned idx = p->cnt - eleCnt; // index of the first element to remove
  169. cmStkBlk_t* blkPtr = _cmStBlockIndex(p, &idx ); // get blk and local index of new curBlkPtr
  170. blkPtr->curEleCnt = idx; // update the element cnt for the new curBlkPtr
  171. p->curBlkPtr = blkPtr; // set the new curBlkPtr
  172. blkPtr = blkPtr->link; // for each blk after the current block ...
  173. while( blkPtr != NULL )
  174. {
  175. blkPtr->curEleCnt = 0; // ... set the block empty
  176. blkPtr = blkPtr->link;
  177. }
  178. p->cnt -= eleCnt;
  179. return rc;
  180. }
  181. const void* cmStackTop( cmStackH_t h )
  182. {
  183. unsigned n = cmStackCount(h);
  184. if( n == 0 )
  185. return NULL;
  186. return cmStackGet(h,n-1);
  187. }
  188. cmStRC_t _cmStackSetGet( cmStack_t* p, unsigned index, char* data, unsigned dataEleCnt, bool setFl )
  189. {
  190. cmStRC_t rc = kOkStRC;
  191. if( index + dataEleCnt > p->cnt )
  192. return cmErrMsg(&p->err,kInvalidIdxStRC,"The element index range:%i to %i falls outside the current index range:%i to %i of the stack.",index,index+dataEleCnt-1,0,p->cnt-1);
  193. cmStkBlk_t* blkPtr = _cmStBlockIndex(p,&index);
  194. assert( blkPtr != NULL );
  195. while( dataEleCnt )
  196. {
  197. // calc the count of ele's to copy into this block
  198. unsigned en = cmMin(dataEleCnt, blkPtr->curEleCnt - index );
  199. // calc the count of bytes in en elements
  200. unsigned bn = en * p->eleByteCnt;
  201. char* bp = blkPtr->base + (index * p->eleByteCnt);
  202. // copy the source bytes into the block
  203. memcpy( setFl ? bp:data, setFl ? data:bp, bn );
  204. data = data + bn; // incr the source pointer
  205. dataEleCnt -= en; // decr the source ele count
  206. index = 0; // all blocks after the first use index=0
  207. blkPtr = blkPtr->link;
  208. }
  209. return rc;
  210. }
  211. cmStRC_t cmStackSet( cmStackH_t h, unsigned index, const void* data, unsigned dataEleCnt )
  212. { return _cmStackSetGet( _cmStHandleToPtr(h), index, (char*) data, dataEleCnt,true ); }
  213. cmStRC_t cmStackGetN( cmStackH_t h, unsigned index, void* data, unsigned dataEleCnt )
  214. { return _cmStackSetGet( _cmStHandleToPtr(h), index, (char*)data, dataEleCnt, false ); }
  215. const void* cmStackGet( cmStackH_t h, unsigned index )
  216. {
  217. cmStack_t* p = _cmStHandleToPtr(h);
  218. if( index >= p->cnt )
  219. {
  220. cmErrMsg(&p->err,kInvalidIdxStRC,"The index %i is outside the valid range 0:%i.",index,p->cnt-1);
  221. return NULL;
  222. }
  223. cmStkBlk_t* blkPtr = _cmStBlockIndex(p,&index);
  224. assert( blkPtr != NULL );
  225. return blkPtr->base + (index * p->eleByteCnt);
  226. }
  227. void* cmStackFlatten( cmStackH_t h )
  228. {
  229. cmStack_t* p = _cmStHandleToPtr(h);
  230. cmStkBlk_t* bbp = p->baseBlkPtr;
  231. cmStkBlk_t* cbp = p->curBlkPtr;
  232. unsigned cnt = p->cnt;
  233. // pretend the stack is unallocated
  234. p->baseBlkPtr = NULL;
  235. p->curBlkPtr = NULL;
  236. // allocate a new initial block large enough to hold all the data
  237. cmStkBlk_t* nbp = _cmStkAllocBlk(p,cnt);
  238. cmStkBlk_t* bp = bbp;
  239. unsigned n = 0;
  240. // copy the existing stack values into the new initial block
  241. while( bp != NULL && bp->curEleCnt > 0 )
  242. {
  243. unsigned bn = bp->curEleCnt * p->eleByteCnt;
  244. assert( n < cnt*p->eleByteCnt );
  245. memcpy( nbp->base + n, bp->base, bn );
  246. n += bn;
  247. bp = bp->link;
  248. }
  249. // restore the stack to it's original state, clear and release
  250. p->baseBlkPtr = bbp;
  251. p->curBlkPtr = cbp;
  252. _cmStackClear(p,true);
  253. // setup new stack with the new initial block
  254. // (the data is now linearly arranged in nbp->base)
  255. p->baseBlkPtr = nbp;
  256. p->curBlkPtr = nbp;
  257. p->cnt = cnt;
  258. return p->baseBlkPtr->base;
  259. }
  260. void cmStackTest( cmCtx_t* ctx )
  261. {
  262. cmStackH_t h = cmStackNullHandle;
  263. int i;
  264. int v[20];
  265. if( cmStackAlloc(ctx,&h,10,5,sizeof(int)) != kOkStRC )
  266. goto errLabel;
  267. for(i=0; i<9; ++i)
  268. v[i] = i;
  269. cmStackPush(h,v,i);
  270. cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i);
  271. for(; i<18; ++i)
  272. v[i] = i;
  273. cmStackPush(h,v,i);
  274. cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i);
  275. cmStackPop(h,10);
  276. cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i);
  277. cmStackPop(h,10);
  278. cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i);
  279. cmStackPush(h,v,10);
  280. cmRptPrintf(&ctx->rpt,"count:%i %i\n",cmStackCount(h),i);
  281. for(i=0; i<cmStackCount(h); ++i)
  282. cmRptPrintf(&ctx->rpt,"%i ",cmStackEle(h,int,i));
  283. cmRptPrintf(&ctx->rpt,"\n");
  284. int* ip = (int*)cmStackFlatten(h);
  285. for(i=0; i<cmStackCount(h); ++i)
  286. cmRptPrintf(&ctx->rpt,"%i ",ip[i]);
  287. cmRptPrintf(&ctx->rpt,"\n");
  288. errLabel:
  289. cmStackFree(&h);
  290. }