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

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