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.

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