libcm is a C development framework with an emphasis on audio signal processing applications.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

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