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.

cmDList.c 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686
  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 "cmGlobal.h"
  4. #include "cmRpt.h"
  5. #include "cmErr.h"
  6. #include "cmCtx.h"
  7. #include "cmMem.h"
  8. #include "cmMallocDebug.h"
  9. #include "cmDList.h"
  10. typedef struct cmDListRecd_str
  11. {
  12. void* dV;
  13. unsigned dN;
  14. struct cmDListRecd_str* prev;
  15. struct cmDListRecd_str* next;
  16. } cmDListRecd_t;
  17. typedef struct cmDListIndexRecd_str
  18. {
  19. cmDListRecd_t* r;
  20. struct cmDListIndexRecd_str* prev;
  21. struct cmDListIndexRecd_str* next;
  22. } cmDListIndexRecd_t;
  23. typedef struct cmDListIndex_str
  24. {
  25. unsigned id;
  26. cmDListCmpFunc_t cmpFunc;
  27. void* funcArg;
  28. cmDListIndexFreeFunc_t freeFunc;
  29. cmDListIndexRecd_t* first;
  30. cmDListIndexRecd_t* last;
  31. unsigned recdN;
  32. struct cmDListIndex_str* link;
  33. } cmDListIndex_t;
  34. struct cmDList_str;
  35. typedef struct cmDListIter_str
  36. {
  37. struct cmDList_str* p; // pointer to the owner cmDList
  38. cmDListIndex_t* x; // pointer to the index this iterator traverses
  39. cmDListIndexRecd_t* s; // current record
  40. struct cmDListIter_str* link; // p->iters list link
  41. } cmDListIter_t;
  42. typedef struct cmDList_str
  43. {
  44. cmErr_t err;
  45. cmDListRecd_t* first;
  46. cmDListRecd_t* last;
  47. unsigned recdN;
  48. cmDListIndex_t* indexes;
  49. cmDListIter_t* iters;
  50. } cmDList_t;
  51. cmDListH_t cmDListNullHandle = cmSTATIC_NULL_HANDLE;
  52. cmDListIterH_t cmDListIterNullHandle = cmSTATIC_NULL_HANDLE;
  53. cmDList_t* _cmDListHandleToPtr( cmDListH_t h )
  54. {
  55. cmDList_t* p = (cmDList_t*)h.h;
  56. assert( p!=NULL );
  57. return p;
  58. }
  59. // Given an indexId return the associated index.
  60. cmDListIndex_t* _cmDListIdToIndex( cmDList_t* p, unsigned indexId )
  61. {
  62. cmDListIndex_t* x = p->indexes;
  63. for(; x!=NULL; x=x->link)
  64. if( x->id == indexId )
  65. return x;
  66. return NULL;
  67. }
  68. // Allocate 'n' new index records for the index 'x'.
  69. void _cmDListIndexAllocRecds( cmDListIndex_t* x, unsigned n )
  70. {
  71. unsigned i;
  72. for(i=0; i<n; ++i)
  73. {
  74. cmDListIndexRecd_t* s = cmMemAllocZ(cmDListIndexRecd_t,1);
  75. s->prev = x->last;
  76. if( x->last != NULL )
  77. x->last->next = s;
  78. else
  79. {
  80. assert( x->first == NULL );
  81. x->first = s;
  82. }
  83. x->last = s;
  84. }
  85. x->recdN += n;
  86. }
  87. void _cmDListRecdFree( cmDList_t* p, cmDListRecd_t* r )
  88. {
  89. if( r->prev != NULL )
  90. r->prev->next = r->next;
  91. else
  92. p->first = r->next;
  93. if( r->next != NULL )
  94. r->next->prev = r->prev;
  95. else
  96. p->last = r->prev;
  97. cmMemFree(r);
  98. }
  99. // Unlink and free the index record s;
  100. void _cmDListIndexRecdFree( cmDListIndex_t* x, cmDListIndexRecd_t* s )
  101. {
  102. if( s->prev != NULL )
  103. s->prev->next = s->next;
  104. else
  105. x->first = s->next;
  106. if( s->next != NULL )
  107. s->next->prev = s->prev;
  108. else
  109. x->last = s->prev;
  110. cmMemFree(s);
  111. }
  112. // Unlink and release an index.
  113. void _cmDListIndexFree( cmDList_t* p, cmDListIndex_t* x )
  114. {
  115. if( x == NULL )
  116. return;
  117. cmDListIndex_t* x0 = NULL;
  118. cmDListIndex_t* x1 = p->indexes;
  119. // unlink 'x' from p->indexes
  120. while( x1 != NULL )
  121. {
  122. if( x1 == x )
  123. {
  124. // x is the first index
  125. if( x0 == NULL )
  126. {
  127. assert( x1 == p->indexes );
  128. p->indexes = x->link;
  129. }
  130. else
  131. {
  132. x0->link = x1->link;
  133. }
  134. break;
  135. }
  136. x0 = x1;
  137. x1 = x1->link;
  138. }
  139. // 'x' must have been found
  140. assert( x1 == x );
  141. // release each index record in 'x'
  142. cmDListIndexRecd_t* s = x->first;
  143. while( s != NULL )
  144. {
  145. cmDListIndexRecd_t* ns = s->next;
  146. cmMemFree(s);
  147. s = ns;
  148. }
  149. if( x->freeFunc != NULL )
  150. x->freeFunc(x->id,x->funcArg);
  151. cmMemFree(x);
  152. }
  153. // Unlink and release the iterator 'e'.
  154. cmDlRC_t _cmDListIterFree( cmDListIter_t* e )
  155. {
  156. cmDList_t* p = e->p;
  157. cmDListIter_t* e0 = NULL;
  158. cmDListIter_t* e1 = p->iters;
  159. while( e1 != NULL )
  160. {
  161. if( e1 == e )
  162. {
  163. if( e0 == NULL )
  164. p->iters = e1->link;
  165. else
  166. e0->link = e1->link;
  167. cmMemFree(e1);
  168. break;
  169. }
  170. e0 = e1;
  171. e1 = e1->link;
  172. }
  173. if( e1 == NULL )
  174. return cmErrMsg(&p->err,kIterNotFoundDlRC,"The delete target iterator could not be found.");
  175. return kOkDlRC;
  176. }
  177. cmDlRC_t _cmDListFree( cmDList_t* p )
  178. {
  179. cmDlRC_t rc = kOkDlRC;
  180. // release all indexes
  181. while( p->indexes != NULL )
  182. _cmDListIndexFree(p,p->indexes);
  183. while( p->iters != NULL )
  184. _cmDListIterFree(p->iters);
  185. // release all data records
  186. while( p->first != NULL )
  187. _cmDListRecdFree(p,p->first);
  188. cmMemFree(p);
  189. return rc;
  190. }
  191. void _cmDListIndexUpdate( cmDList_t* p, cmDListIndex_t* x )
  192. {
  193. cmDListIndexRecd_t* first = NULL;
  194. cmDListIndexRecd_t* last = NULL;
  195. assert( x->recdN >= p->recdN );
  196. // for each data recd
  197. cmDListRecd_t* r = p->first;
  198. for(; r!=NULL; r=r->next)
  199. {
  200. // get the next available index record
  201. cmDListIndexRecd_t* a = x->first;
  202. assert(a!=NULL);
  203. x->first = x->first->next;
  204. if( x->first != NULL )
  205. x->first->prev = NULL;
  206. // The count of index records and data records should always be the same.
  207. assert( a != NULL );
  208. a->r = r;
  209. cmDListIndexRecd_t* s = first;
  210. // for each index recd that has already been sorted
  211. for(; s!=NULL; s=s->next)
  212. if( x->cmpFunc( x->funcArg, r->dV, r->dN, s->r->dV, s->r->dN ) < 0 )
  213. {
  214. // r is less than s->r
  215. // insert 'a' prior to 's' in the index
  216. a->next = s;
  217. a->prev = s->prev;
  218. // if 's' is not first
  219. if( s->prev != NULL )
  220. s->prev->next = a;
  221. else
  222. { // 's' was first - now 'a' is first
  223. assert( s == first );
  224. first = a;
  225. }
  226. s->prev = a;
  227. break;
  228. }
  229. // No records are greater than r or the index is empty - 'a' is last in the index.
  230. if( s == NULL )
  231. {
  232. // insert 'a' after 'last'
  233. // if the index is empty
  234. if( last == NULL )
  235. {
  236. first = a;
  237. a->prev = NULL;
  238. }
  239. else // make 'a' last in the index
  240. {
  241. a->prev = last;
  242. a->next = NULL;
  243. assert( last->next == NULL );
  244. last->next = a;
  245. }
  246. a->next = NULL;
  247. last = a;
  248. }
  249. }
  250. // release any index records that are not in use
  251. while(x->first!=NULL)
  252. {
  253. _cmDListIndexRecdFree(x,x->first);
  254. x->recdN -= 1;
  255. }
  256. assert( x->recdN == p->recdN );
  257. // Invalidate all iterators which use index x.
  258. cmDListIter_t* e = p->iters;
  259. for(; e!=NULL; e=e->link)
  260. if( e->x == x )
  261. e->s = NULL;
  262. x->first = first;
  263. x->last = last;
  264. }
  265. cmDlRC_t _cmDListIndexAlloc( cmDList_t* p, unsigned indexId, cmDListCmpFunc_t func, void* funcArg )
  266. {
  267. cmDListIndex_t* x;
  268. if((x =_cmDListIdToIndex(p, indexId )) != NULL )
  269. return cmErrMsg(&p->err,kDuplicateIndexIdDlRC,"The indexId '%i' has already been used.",indexId);
  270. x = cmMemAllocZ(cmDListIndex_t,1);
  271. x->id = indexId;
  272. x->cmpFunc = func;
  273. x->funcArg = funcArg;
  274. x->link = p->indexes;
  275. p->indexes = x;
  276. _cmDListIndexAllocRecds(x,p->recdN);
  277. _cmDListIndexUpdate(p,x);
  278. return kOkDlRC;
  279. }
  280. cmDlRC_t cmDListAlloc( cmCtx_t* ctx, cmDListH_t* hp, cmDListCmpFunc_t func, void* funcArg )
  281. {
  282. cmDlRC_t rc = kOkDlRC;
  283. if((rc = cmDListFree(hp)) != kOkDlRC )
  284. return rc;
  285. cmDList_t* p = cmMemAllocZ(cmDList_t,1);
  286. cmErrSetup(&p->err,&ctx->rpt,"cmDList");
  287. if( func!=NULL )
  288. if((rc = _cmDListIndexAlloc(p,0,func,funcArg)) != kOkDlRC )
  289. goto errLabel;
  290. hp->h = p;
  291. errLabel:
  292. if( rc != kOkDlRC )
  293. _cmDListFree(p);
  294. return rc;
  295. }
  296. cmDlRC_t cmDListFree( cmDListH_t* hp )
  297. {
  298. cmDlRC_t rc;
  299. if( hp==NULL || cmDListIsValid(*hp)==false)
  300. return kOkDlRC;
  301. cmDList_t* p = _cmDListHandleToPtr(*hp);
  302. if((rc = _cmDListFree(p)) != kOkDlRC )
  303. return rc;
  304. hp->h = NULL;
  305. return rc;
  306. }
  307. bool cmDListIsValid( cmDListH_t h )
  308. { return h.h != NULL; }
  309. cmDlRC_t cmDListInsert( cmDListH_t h, const void* recd, unsigned recdByteN, bool resyncFl )
  310. {
  311. cmDList_t* p = _cmDListHandleToPtr(h);
  312. char* vp = cmMemAllocZ(char,sizeof(cmDListRecd_t) + recdByteN );
  313. cmDListRecd_t* r = (cmDListRecd_t*)vp;
  314. r->dV = r + 1;
  315. r->dN = recdByteN;
  316. memcpy( r->dV, recd, recdByteN );
  317. // Add records at the end of the data record list.
  318. // If the list is not empty
  319. if( p->last != NULL )
  320. p->last->next = r;
  321. else
  322. {
  323. // The list was empty
  324. assert( p->first == NULL );
  325. p->first = r;
  326. }
  327. r->prev = p->last;
  328. r->next = NULL;
  329. p->last = r;
  330. p->recdN += 1;
  331. // add a record to each index
  332. cmDListIndex_t* x = p->indexes;
  333. for(; x!=NULL; x=x->link)
  334. {
  335. if( x->recdN < p->recdN )
  336. _cmDListIndexAllocRecds(x,1);
  337. assert( x->recdN >= p->recdN );
  338. if( resyncFl )
  339. _cmDListIndexUpdate(p,x);
  340. }
  341. return kOkDlRC;
  342. }
  343. cmDlRC_t cmDListDelete( cmDListH_t h, const void* recd, bool resyncFl )
  344. {
  345. cmDList_t* p = _cmDListHandleToPtr(h);
  346. cmDListIndex_t* x = p->indexes;
  347. cmDListIndexRecd_t* s = NULL;
  348. cmDListRecd_t* r = NULL;
  349. if( resyncFl==false )
  350. {
  351. r = p->first;
  352. for(; r!=NULL; r=r->next)
  353. if( r->dV == recd )
  354. {
  355. _cmDListRecdFree(p,r);
  356. break;
  357. }
  358. }
  359. else
  360. {
  361. // for each index
  362. for(; x!=NULL; x=x->link)
  363. {
  364. // for each index recd
  365. for(s=x->first; s!=NULL; s=s->next)
  366. if( s->r->dV == recd ) // if this index recd points to the deletion target
  367. {
  368. // store a ptr to the data recd to be deleted
  369. if( r == NULL )
  370. r = s->r;
  371. else
  372. {
  373. // the same data record should be found for all indexes
  374. assert( s->r == r );
  375. }
  376. // free the index record
  377. _cmDListIndexRecdFree(x,s);
  378. break;
  379. }
  380. // if the deletion target was not found
  381. if( r == NULL )
  382. goto errLabel;
  383. }
  384. // advance any iterators that are pointing to the deleted record
  385. cmDListIter_t* e = p->iters;
  386. for(; e!=NULL; e=e->link)
  387. if( e->s != NULL && e->s->r != NULL && e->s->r == r )
  388. e->s = e->s->next;
  389. // release the data record
  390. _cmDListRecdFree(p,r);
  391. }
  392. errLabel:
  393. if( r == NULL )
  394. return cmErrMsg(&p->err,kDataRecdNotFoundDlRC,"The deletion target record could not be found.");
  395. assert( p->recdN > 0 );
  396. p->recdN -= 1;
  397. return kOkDlRC;
  398. }
  399. cmDlRC_t cmDListIndexAlloc( cmDListH_t h, unsigned indexId, cmDListCmpFunc_t func, void* funcArg )
  400. {
  401. cmDList_t* p = _cmDListHandleToPtr(h);
  402. return _cmDListIndexAlloc(p,indexId,func,funcArg);
  403. }
  404. cmDlRC_t cmDListIndexFree( cmDListH_t h, unsigned indexId )
  405. {
  406. cmDList_t* p = _cmDListHandleToPtr(h);
  407. cmDListIndex_t* x;
  408. if((x = _cmDListIdToIndex(p,indexId)) == NULL )
  409. return cmErrMsg(&p->err,kInvalidIndexDlRC,"The indexId '%i' could not be found.",indexId);
  410. _cmDListIndexFree(p,x);
  411. return kOkDlRC;
  412. }
  413. cmDlRC_t cmDListIndexSetFreeFunc(cmDListH_t h, unsigned indexId, cmDListIndexFreeFunc_t func )
  414. {
  415. cmDList_t* p = _cmDListHandleToPtr(h);
  416. cmDListIndex_t* x;
  417. if((x = _cmDListIdToIndex(p,indexId)) == NULL )
  418. return cmErrMsg(&p->err,kInvalidIndexDlRC,"The indexId '%i' could not be found.",indexId);
  419. x->freeFunc = func;
  420. return kOkDlRC;
  421. }
  422. cmDlRC_t cmDListIndexUpdateAll( cmDListH_t h )
  423. {
  424. cmDList_t* p = _cmDListHandleToPtr(h);
  425. cmDListIndex_t* x = p->indexes;
  426. for(; x!=NULL; x=x->link)
  427. _cmDListIndexUpdate(p,x);
  428. return kOkDlRC;
  429. }
  430. cmDListIter_t* _cmDListIterHandleToPtr( cmDListIterH_t h )
  431. {
  432. cmDListIter_t* e = (cmDListIter_t*)h.h;
  433. assert(e != NULL );
  434. return e;
  435. }
  436. cmDlRC_t cmDListIterAlloc( cmDListH_t h, cmDListIterH_t* iHp, unsigned indexId )
  437. {
  438. cmDlRC_t rc = kOkDlRC;
  439. cmDList_t* p = _cmDListHandleToPtr(h);
  440. cmDListIndex_t* x;
  441. if((rc = cmDListIterFree(iHp)) != kOkDlRC )
  442. return rc;
  443. if((x = _cmDListIdToIndex(p, indexId)) == NULL )
  444. return cmErrMsg(&p->err,kInvalidIndexDlRC,"The indexId '%i' could not be found.",indexId);
  445. cmDListIter_t* e = cmMemAllocZ(cmDListIter_t,1);
  446. e->p = p;
  447. e->x = x;
  448. e->s = x->first;
  449. e->link = p->iters;
  450. p->iters = e;
  451. iHp->h = e;
  452. return rc;
  453. }
  454. cmDlRC_t cmDListIterFree( cmDListIterH_t* iHp )
  455. {
  456. cmDlRC_t rc;
  457. if( iHp==NULL || cmDListIterIsValid(*iHp)==false )
  458. return kOkDlRC;
  459. cmDListIter_t* e = _cmDListIterHandleToPtr(*iHp);
  460. if(( rc = _cmDListIterFree( e )) != kOkDlRC )
  461. return rc;
  462. iHp->h = NULL;
  463. return rc;
  464. }
  465. bool cmDListIterIsValid( cmDListIterH_t iH )
  466. { return iH.h != NULL; }
  467. cmDlRC_t cmDListIterSeekBegin( cmDListIterH_t iH )
  468. {
  469. cmDListIter_t* e = _cmDListIterHandleToPtr(iH);
  470. e->s = e->x->first;
  471. return kOkDlRC;
  472. }
  473. cmDlRC_t cmDListIterSeekLast( cmDListIterH_t iH )
  474. {
  475. cmDListIter_t* e = _cmDListIterHandleToPtr(iH);
  476. e->s = e->x->last;
  477. return kOkDlRC;
  478. }
  479. const void* _cmDListIterGet( cmDListIter_t* e, unsigned* recdByteNRef )
  480. {
  481. if( e->s == NULL )
  482. {
  483. if( recdByteNRef != NULL )
  484. *recdByteNRef = 0;
  485. return NULL;
  486. }
  487. assert( e->s->r != NULL );
  488. if( recdByteNRef != NULL )
  489. *recdByteNRef = e->s->r->dN;
  490. return e->s->r->dN==0 ? NULL : e->s->r->dV;
  491. }
  492. const void* cmDListIterGet( cmDListIterH_t iH, unsigned* recdByteNRef )
  493. {
  494. cmDListIter_t* e = _cmDListIterHandleToPtr(iH);
  495. return _cmDListIterGet(e,recdByteNRef);
  496. }
  497. const void* cmDListIterPrev( cmDListIterH_t iH, unsigned* recdByteNRef )
  498. {
  499. cmDListIter_t* e = _cmDListIterHandleToPtr(iH);
  500. const void* rv = _cmDListIterGet(e,recdByteNRef);
  501. if( e->s != NULL )
  502. e->s = e->s->prev;
  503. return rv;
  504. }
  505. const void* cmDListIterNext( cmDListIterH_t iH, unsigned* recdByteNRef )
  506. {
  507. cmDListIter_t* e = _cmDListIterHandleToPtr(iH);
  508. const void* rv = _cmDListIterGet(e,recdByteNRef);
  509. if( e->s != NULL )
  510. e->s = e->s->next;
  511. return rv;
  512. }
  513. const void* cmDListIterFind( cmDListIterH_t iH, const void* key, unsigned keyN, unsigned* recdByteNRef)
  514. {
  515. cmDListIter_t* e = _cmDListIterHandleToPtr(iH);
  516. cmDListIndexRecd_t* s = e->s;
  517. for(; s!=NULL; s=s->next)
  518. if( e->x->cmpFunc( e->x->funcArg, s->r->dV, s->r->dN, key, keyN ) == 0 )
  519. {
  520. e->s = s;
  521. return _cmDListIterGet(e,recdByteNRef);
  522. }
  523. if( recdByteNRef != NULL )
  524. *recdByteNRef = 0;
  525. return NULL;
  526. }