libcm is a C development framework with an emphasis on audio signal processing applications.
Du kannst nicht mehr als 25 Themen auswählen Themen müssen mit entweder einem Buchstaben oder einer Ziffer beginnen. Sie können Bindestriche („-“) enthalten und bis zu 35 Zeichen lang sein.

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. }