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.

cmHashTbl.c 10KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466
  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 "cmFloatTypes.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. #include "cmMath.h"
  12. #include "cmHashTbl.h"
  13. #include "cmText.h"
  14. enum
  15. {
  16. kFreeHtFl = 0x01,
  17. };
  18. typedef struct cmHtValue_str
  19. {
  20. unsigned flags; // See kXXXHtFl above.
  21. unsigned id; // unique id associated with this value
  22. void* value; // value blob
  23. unsigned byteCnt; // size of value blob in bytes
  24. struct cmHtValue_str* link; // cmHtBucket_t.list link
  25. } cmHtValue_t;
  26. typedef struct
  27. {
  28. cmHtValue_t* list; // value list
  29. cmHtValue_t* avail; // available value slots - formed from cmHashTblRemoved() values.
  30. unsigned nextIdx; // next unused index for this bucket
  31. } cmHtBucket_t;
  32. typedef struct
  33. {
  34. cmErr_t err;
  35. cmLHeapH_t lhH; // memory for hash table buckets, values, value blobs.
  36. unsigned bucketCnt; // hash table bucket cnt
  37. unsigned linkCnt; // max length of collision list for each bucket
  38. unsigned mask; // hash id bucket index mask (masks the MSB's of the hash-id)
  39. unsigned maskShift; // shift required to move the lowest 'mask' bit to the LSB.
  40. cmHtBucket_t* b; // b[bucketCnt] bucket array
  41. } cmHt_t;
  42. cmHashTblH_t cmHashTblNullHandle = cmSTATIC_NULL_HANDLE;
  43. #define _cmHtBucketIndex( p, id ) (((id) & (p)->mask) >> (p)->maskShift)
  44. cmHt_t* _cmHtHandleToPtr( cmHashTblH_t h )
  45. {
  46. cmHt_t* p = (cmHt_t*)h.h;
  47. assert(p!=NULL);
  48. return p;
  49. }
  50. // Return the bucket index portion of the hash id.
  51. unsigned _cmHtGenId( cmHt_t* p, const void* v, unsigned byteCnt )
  52. {
  53. unsigned i,j;
  54. const char* cv = v;
  55. unsigned h = 0;
  56. for(i=0,j=3; i<byteCnt; ++i,++j)
  57. h += ((unsigned)cv[i]) << ((j&0x3)*8);
  58. return h & p->mask;
  59. }
  60. // Given an id find the value.
  61. cmHtValue_t* _cmHtIdToValue( cmHt_t* p, unsigned id )
  62. {
  63. if( id == cmInvalidId )
  64. return NULL;
  65. unsigned bi = _cmHtBucketIndex(p,id);
  66. assert(bi < p->bucketCnt);
  67. cmHtValue_t* v = p->b[bi].list;
  68. for(; v!=NULL; v=v->link)
  69. if( v->id == id )
  70. return v;
  71. return NULL;
  72. }
  73. // Given a value find the id
  74. cmHtValue_t* _cmHtValueToId( cmHt_t* p, const void* value, unsigned byteCnt, unsigned id )
  75. {
  76. if( id == cmInvalidId )
  77. id = _cmHtGenId(p,value,byteCnt);
  78. unsigned bi = _cmHtBucketIndex(p,id);
  79. assert(bi < p->bucketCnt);
  80. cmHtValue_t* v = p->b[bi].list;
  81. for(; v!=NULL; v=v->link)
  82. if( v->byteCnt==byteCnt && memcmp(value,v->value,byteCnt)==0 )
  83. return v;
  84. return NULL;
  85. }
  86. cmHtRC_t _cmHtDestroy( cmHt_t* p )
  87. {
  88. cmHtRC_t rc = kOkHtRC;
  89. cmLHeapDestroy(&p->lhH);
  90. cmMemFree(p->b);
  91. cmMemFree(p);
  92. return rc;
  93. }
  94. cmHtRC_t cmHashTblCreate( cmCtx_t* ctx, cmHashTblH_t* hp, unsigned bucketCnt )
  95. {
  96. cmHtRC_t rc;
  97. if((rc = cmHashTblDestroy(hp)) != kOkHtRC )
  98. return rc;
  99. cmHt_t* p = cmMemAllocZ(cmHt_t,1);
  100. cmErrSetup(&p->err,&ctx->rpt,"hash table");
  101. if(cmLHeapIsValid(p->lhH = cmLHeapCreate(8192,ctx)) == false )
  102. {
  103. cmErrMsg(&p->err,kLHeapFailHtRC,"Internal linked heap mgr. create failed.");
  104. goto errLabel;
  105. }
  106. // force the bucket count to be a power of two
  107. p->bucketCnt = cmNextPowerOfTwo(bucketCnt);
  108. p->mask = p->bucketCnt - 1;
  109. // calcluate the hash-id bucket mask
  110. for(p->maskShift=0; (0x80000000 & p->mask) == 0; ++p->maskShift )
  111. p->mask <<= 1;
  112. // calculate the maximum collisions per bucket mask
  113. p->linkCnt = ~p->mask;
  114. // allocate the bucket array
  115. p->b = cmMemAllocZ(cmHtBucket_t,p->bucketCnt);
  116. hp->h = p;
  117. errLabel:
  118. if( rc != kOkHtRC )
  119. _cmHtDestroy(p);
  120. return rc;
  121. }
  122. cmHtRC_t cmHashTblDestroy( cmHashTblH_t* hp )
  123. {
  124. cmHtRC_t rc = kOkHtRC;
  125. if(hp==NULL || cmHashTblIsValid(*hp)==false )
  126. return rc;
  127. cmHt_t* p = _cmHtHandleToPtr(*hp);
  128. if((rc = _cmHtDestroy(p)) != kOkHtRC )
  129. return rc;
  130. hp->h = NULL;
  131. return rc;
  132. }
  133. bool cmHashTblIsValid( cmHashTblH_t h )
  134. { return h.h!=NULL; }
  135. unsigned cmHashTblStoreBase( cmHashTblH_t h, void* v, unsigned byteCnt, bool staticFl )
  136. {
  137. cmHt_t* p = _cmHtHandleToPtr(h);
  138. cmHtValue_t* vp = NULL;
  139. unsigned id = _cmHtGenId(p, v, byteCnt );
  140. // if the value is already stored then there is nothing else to do
  141. if((vp = _cmHtValueToId(p,v,byteCnt,id)) != NULL )
  142. return vp->id;
  143. unsigned bi = _cmHtBucketIndex(p,id);
  144. assert(bi < p->bucketCnt );
  145. cmHtBucket_t* b = p->b + bi;
  146. if( b->avail != NULL )
  147. {
  148. vp = b->avail;
  149. b->avail = b->avail->link;
  150. }
  151. else
  152. {
  153. if( b->nextIdx == p->linkCnt || (id + b->nextIdx) == cmInvalidId )
  154. {
  155. cmErrMsg(&p->err,kHashFaultHtRC,"The hash table bucket at index %i is exhaused.",bi);
  156. return cmInvalidId;
  157. }
  158. vp = cmLhAllocZ(p->lhH,cmHtValue_t,1);
  159. vp->id = id + b->nextIdx++;
  160. }
  161. assert( vp->id != cmInvalidId );
  162. vp->link = b->list;
  163. b->list = vp;
  164. vp->byteCnt = byteCnt;
  165. if( staticFl )
  166. vp->value = v;
  167. else
  168. {
  169. vp->value = cmLhAlloc(p->lhH,char,byteCnt);
  170. memcpy(vp->value,v,byteCnt);
  171. vp->flags = cmSetFlag(vp->flags,kFreeHtFl);
  172. }
  173. return vp->id;
  174. }
  175. unsigned cmHashTblStore( cmHashTblH_t h, void* v, unsigned byteCnt )
  176. { return cmHashTblStoreBase(h,v,byteCnt,false); }
  177. unsigned cmHashTblStoreStatic( cmHashTblH_t h, void* v, unsigned byteCnt )
  178. { return cmHashTblStoreBase(h,v,byteCnt,true); }
  179. unsigned _cmHashTblStoreStr( cmHashTblH_t h, const cmChar_t* s, bool staticFl )
  180. {
  181. unsigned n = cmTextLength(s);
  182. if( n == 0 )
  183. {
  184. s = "";
  185. n = 1;
  186. }
  187. return cmHashTblStoreBase(h,(void*)s,n+1,staticFl);
  188. }
  189. unsigned cmHashTblStoreStr( cmHashTblH_t h, const cmChar_t* s )
  190. { return _cmHashTblStoreStr(h,s,false); }
  191. unsigned cmhashTblStoreStaticStr( cmHashTblH_t h, const cmChar_t* s )
  192. { return _cmHashTblStoreStr(h,s,true); }
  193. unsigned cmHashTblStoreV( cmHashTblH_t h, const cmChar_t* fmt, va_list vl )
  194. {
  195. cmChar_t* s = NULL;
  196. s = cmTsVPrintfP(s,fmt,vl);
  197. unsigned id = _cmHashTblStoreStr(h,s,false);
  198. cmMemFree(s);
  199. return id;
  200. }
  201. unsigned cmHashTblStoreF( cmHashTblH_t h, const cmChar_t* fmt, ... )
  202. {
  203. va_list vl;
  204. va_start(vl,fmt);
  205. unsigned id = cmHashTblStoreV(h,fmt,vl);
  206. va_end(vl);
  207. return id;
  208. }
  209. unsigned cmHashTblId( cmHashTblH_t h, const void* value, unsigned byteCnt )
  210. {
  211. cmHt_t* p = _cmHtHandleToPtr(h);
  212. cmHtValue_t* vp;
  213. if((vp = _cmHtValueToId(p,value,byteCnt,cmInvalidId)) == NULL )
  214. return cmInvalidId;
  215. return vp->id;
  216. }
  217. unsigned cmHashTblStrToId( cmHashTblH_t h, const cmChar_t* str )
  218. {
  219. if( str == NULL )
  220. return cmInvalidId;
  221. return cmHashTblId(h,str,cmTextLength(str)+1);
  222. }
  223. const void* cmHashTblValue( cmHashTblH_t h, unsigned id, unsigned* byteCntRef )
  224. {
  225. cmHt_t* p = _cmHtHandleToPtr(h);
  226. cmHtValue_t* vp;
  227. if((vp = _cmHtIdToValue(p, id)) != NULL )
  228. {
  229. if( byteCntRef != NULL )
  230. *byteCntRef = vp->byteCnt;
  231. return vp->value;
  232. }
  233. return NULL;
  234. }
  235. const cmChar_t* cmHashTblStr( cmHashTblH_t h, unsigned id )
  236. { return (const cmChar_t*)cmHashTblValue(h,id,NULL); }
  237. cmHtRC_t cmHashTblRemove( cmHashTblH_t h, unsigned id )
  238. {
  239. cmHt_t* p = _cmHtHandleToPtr(h);
  240. unsigned bi = _cmHtBucketIndex(p,id);
  241. assert(bi < p->bucketCnt);
  242. cmHtBucket_t* b = p->b + bi;
  243. cmHtValue_t* vp = b->list;
  244. cmHtValue_t* pp = NULL;
  245. for(; vp!=NULL; vp=vp->link)
  246. {
  247. if( vp->id == id )
  248. {
  249. if( pp == NULL )
  250. b->list = vp->link;
  251. else
  252. pp->link = vp->link;
  253. break;
  254. }
  255. pp = vp;
  256. }
  257. if( vp == NULL )
  258. return cmErrMsg(&p->err,kInvalidIdHtRC,"A value could not be found for the hash id 0x%x.",id);
  259. if( cmIsFlag(vp->flags,kFreeHtFl ) )
  260. cmLhFree(p->lhH,vp->value);
  261. vp->flags = 0;
  262. vp->value = NULL;
  263. vp->byteCnt = 0;
  264. // Note: Do not set the id to zero since we want to consert id's
  265. // and this recd will be reused by the next call to cmHashTblStoreBase().
  266. return kOkHtRC;
  267. }
  268. cmHtRC_t cmHashTblLastRC( cmHashTblH_t h )
  269. {
  270. cmHt_t* p = _cmHtHandleToPtr(h);
  271. return cmErrLastRC(&p->err);
  272. }
  273. void _cmHashTblBucketReport( cmHtBucket_t* b, cmRpt_t* rpt )
  274. {
  275. cmHtValue_t* vp = b->list;
  276. unsigned i;
  277. for(i=0; vp!=NULL && i<10; vp=vp->link,++i)
  278. cmRptPrintf(rpt,"0x%x : %s\n",vp->id,((const cmChar_t*)vp->value));
  279. cmRptPrintf(rpt,"\n");
  280. }
  281. void cmHashTblReport( cmHashTblH_t h, cmRpt_t* rpt )
  282. {
  283. cmHt_t* p = _cmHtHandleToPtr(h);
  284. unsigned i;
  285. for(i=0; i<p->bucketCnt; ++i)
  286. {
  287. //if( p->b[i].nextIdx > 0 )
  288. // cmRptPrintf(rpt,"%i,%i\n",i,p->b[i].nextIdx);
  289. if( p->b[i].nextIdx > 100 )
  290. _cmHashTblBucketReport(p->b + i,rpt);
  291. }
  292. }
  293. cmHtRC_t cmHashTblTest( cmCtx_t* ctx )
  294. {
  295. cmHtRC_t rc = kOkHtRC;
  296. cmHashTblH_t h = cmHashTblNullHandle;
  297. cmErr_t err;
  298. cmErrSetup(&err,&ctx->rpt,"hash table test");
  299. if((rc = cmHashTblCreate(ctx,&h,8192)) != kOkHtRC )
  300. return cmErrMsg(&err,rc,"Hash table create failed.");
  301. const cmChar_t* arr[] =
  302. {
  303. "1",
  304. "12",
  305. "123",
  306. "1234",
  307. "12345",
  308. "123456",
  309. "123456",
  310. "123456",
  311. NULL
  312. };
  313. unsigned n = sizeof(arr)/sizeof(arr[0]);
  314. unsigned ids[ n ];
  315. int i = 0;
  316. // store the values from arr[]
  317. for(; arr[i]!=NULL; ++i)
  318. if((ids[i] = cmHashTblStoreStr(h,arr[i])) == cmInvalidId )
  319. {
  320. rc = cmErrMsg(&err,cmHashTblLastRC(h),"Hash store failed on: '%s.",cmStringNullGuard(arr[i]));
  321. goto errLabel;
  322. }
  323. /*
  324. // remove a value
  325. unsigned rem_idx = 3;
  326. if((rc = cmHashTblRemove(h, ids[rem_idx] )) != kOkHtRC )
  327. {
  328. rc = cmErrMsg(&err,rc,"Hash removed failed.");
  329. goto errLabel;
  330. }
  331. // insert the same value - which should restore the removed value
  332. if((ids[rem_idx] = cmHashTblStoreStr(h,arr[rem_idx])) == cmInvalidId )
  333. {
  334. rc = cmErrMsg(&err,cmHashTblLastRC(h),"Hash store failed on: '%s.",cmStringNullGuard(arr[rem_idx]));
  335. goto errLabel;
  336. }
  337. */
  338. // lookup all the stored values by id
  339. for(--i; i>=0; --i)
  340. {
  341. const cmChar_t* s;
  342. if((s = cmHashTblStr(h,ids[i])) == NULL )
  343. rc = cmErrMsg(&err,kInvalidIdHtRC,"The value associated with hash-id:0x%x could not be found.",ids[i]);
  344. else
  345. printf("%i : %s\n",i,cmStringNullGuard(s));
  346. }
  347. for(i=0; arr[i]!=NULL; ++i)
  348. {
  349. unsigned id = cmHashTblStrToId(h, arr[i]);
  350. printf("%i : 0x%x : %s\n",i, id, cmStringNullGuard(cmHashTblStr(h, id)));
  351. }
  352. cmHashTblReport(h, &ctx->rpt );
  353. errLabel:
  354. cmHashTblDestroy(&h);
  355. return rc;
  356. }