IntHash.hx 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /*
  2. * Copyright (C)2005-2012 Haxe Foundation
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a
  5. * copy of this software and associated documentation files (the "Software"),
  6. * to deal in the Software without restriction, including without limitation
  7. * the rights to use, copy, modify, merge, publish, distribute, sublicense,
  8. * and/or sell copies of the Software, and to permit persons to whom the
  9. * Software is furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  19. * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
  20. * DEALINGS IN THE SOFTWARE.
  21. */
  22. import java.NativeArray;
  23. /*
  24. * This IntHash implementation is based on khash (https://github.com/attractivechaos/klib/blob/master/khash.h)
  25. * Copyright goes to Attractive Chaos <[email protected]> and his contributors
  26. *
  27. * Thanks also to Jonas Malaco Filho for his Haxe-written IntHash code inspired by Python tables.
  28. * (https://jonasmalaco.com/fossil/test/jonas-haxe/artifact/887b53126e237d6c68951111d594033403889304)
  29. */
  30. @:coreApi class IntHash<T>
  31. {
  32. private static inline var HASH_UPPER = 0.7;
  33. private var flags:NativeArray<Int>;
  34. private var _keys:NativeArray<Int>;
  35. private var vals:NativeArray<T>;
  36. private var nBuckets:Int;
  37. private var size:Int;
  38. private var nOccupied:Int;
  39. private var upperBound:Int;
  40. private var cachedKey:Int;
  41. private var cachedIndex:Int;
  42. public function new() : Void
  43. {
  44. cachedIndex = -1;
  45. }
  46. public function set( key : Int, value : T ) : Void
  47. {
  48. var x:Int;
  49. if (nOccupied >= upperBound)
  50. {
  51. if (nBuckets > (size << 1))
  52. resize(nBuckets - 1); //clear "deleted" elements
  53. else
  54. resize(nBuckets + 1);
  55. }
  56. var flags = flags, _keys = _keys;
  57. {
  58. var mask = nBuckets - 1;
  59. var site = x = nBuckets;
  60. var k = hash(key);
  61. var i = k & mask;
  62. //for speed up
  63. if (flagIsEmpty(flags, i)) {
  64. x = i;
  65. } else {
  66. var inc = getInc(k, mask);
  67. var last = i;
  68. while (! (isEither(flags, i) || _keys[i] == key) )
  69. {
  70. i = (i + inc) & mask;
  71. #if DEBUG_HASHTBL
  72. if (i == last)
  73. {
  74. throw "assert";
  75. }
  76. #end
  77. }
  78. x = i;
  79. }
  80. }
  81. if (flagIsEmpty(flags, x))
  82. {
  83. _keys[x] = key;
  84. vals[x] = value;
  85. setIsBothFalse(flags, x);
  86. size++;
  87. nOccupied++;
  88. } else if (flagIsDel(flags, x)) {
  89. _keys[x] = key;
  90. vals[x] = value;
  91. setIsBothFalse(flags, x);
  92. size++;
  93. } else {
  94. assert(_keys[x] == key);
  95. vals[x] = value;
  96. }
  97. }
  98. @:final private function lookup( key : Int ) : Int
  99. {
  100. if (nBuckets != 0)
  101. {
  102. var flags = flags, _keys = _keys;
  103. var mask = nBuckets - 1, k = hash(key);
  104. var i = k & mask;
  105. var inc = getInc(k, mask); /* inc == 1 for linear probing */
  106. var last = i;
  107. while (!flagIsEmpty(flags, i) && (flagIsDel(flags, i) || _keys[i] != key))
  108. {
  109. i = (i + inc) & mask;
  110. if (i == last)
  111. return -1;
  112. }
  113. return isEither(flags, i) ? -1 : i;
  114. }
  115. return -1;
  116. }
  117. public function get( key : Int ) : Null<T>
  118. {
  119. var idx = -1;
  120. if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
  121. {
  122. return vals[idx];
  123. }
  124. idx = lookup(key);
  125. if (idx != -1)
  126. {
  127. cachedKey = key;
  128. cachedIndex = idx;
  129. return vals[idx];
  130. }
  131. return null;
  132. }
  133. private function getDefault( key : Int, def : T ) : T
  134. {
  135. var idx = -1;
  136. if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
  137. {
  138. return vals[idx];
  139. }
  140. idx = lookup(key);
  141. if (idx != -1)
  142. {
  143. cachedKey = key;
  144. cachedIndex = idx;
  145. return vals[idx];
  146. }
  147. return def;
  148. }
  149. public function exists( key : Int ) : Bool
  150. {
  151. var idx = -1;
  152. if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
  153. {
  154. return true;
  155. }
  156. idx = lookup(key);
  157. if (idx != -1)
  158. {
  159. cachedKey = key;
  160. cachedIndex = idx;
  161. return true;
  162. }
  163. return false;
  164. }
  165. public function remove( key : Int ) : Bool
  166. {
  167. var idx = -1;
  168. if (! (cachedKey == key && ( (idx = cachedIndex) != -1 )))
  169. {
  170. idx = lookup(key);
  171. }
  172. if (idx == -1)
  173. {
  174. return false;
  175. } else {
  176. if (cachedKey == key)
  177. cachedIndex = -1;
  178. if (!isEither(flags, idx))
  179. {
  180. setIsDelTrue(flags, idx);
  181. --size;
  182. vals[idx] = null;
  183. _keys[idx] = 0;
  184. }
  185. return true;
  186. }
  187. }
  188. @:final private function resize(newNBuckets:Int) : Void
  189. {
  190. //This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets.
  191. var newFlags = null;
  192. var j = 1;
  193. {
  194. newNBuckets = roundUp(newNBuckets);
  195. if (newNBuckets < 4) newNBuckets = 4;
  196. if (size >= (newNBuckets * HASH_UPPER + 0.5)) /* requested size is too small */
  197. {
  198. j = 0;
  199. } else { /* hash table size to be changed (shrink or expand); rehash */
  200. var nfSize = flagsSize(newNBuckets);
  201. newFlags = new NativeArray( nfSize );
  202. for (i in 0...nfSize)
  203. newFlags[i] = 0xaaaaaaaa;
  204. if (nBuckets < newNBuckets) //expand
  205. {
  206. var k = new NativeArray(newNBuckets);
  207. if (_keys != null)
  208. arrayCopy(_keys, 0, k, 0, nBuckets);
  209. _keys = k;
  210. var v = new NativeArray(newNBuckets);
  211. if (vals != null)
  212. arrayCopy(vals, 0, v, 0, nBuckets);
  213. vals = v;
  214. } //otherwise shrink
  215. }
  216. }
  217. if (j != 0)
  218. { //rehashing is required
  219. //resetting cache
  220. cachedKey = 0;
  221. cachedIndex = -1;
  222. j = -1;
  223. var nBuckets = nBuckets, _keys = _keys, vals = vals, flags = flags;
  224. var newMask = newNBuckets - 1;
  225. while (++j < nBuckets)
  226. {
  227. if (!isEither(flags, j))
  228. {
  229. var key = _keys[j];
  230. var val = vals[j];
  231. setIsDelTrue(flags, j);
  232. while (true) /* kick-out process; sort of like in Cuckoo hashing */
  233. {
  234. var k = hash(key);
  235. var inc = getInc(k, newMask);
  236. var i = k & newMask;
  237. while (!flagIsEmpty(newFlags, i))
  238. i = (i + inc) & newMask;
  239. setIsEmptyFalse(newFlags, i);
  240. if (i < nBuckets && !isEither(flags, i)) /* kick out the existing element */
  241. {
  242. {
  243. var tmp = _keys[i];
  244. _keys[i] = key;
  245. key = tmp;
  246. }
  247. {
  248. var tmp = vals[i];
  249. vals[i] = val;
  250. val = tmp;
  251. }
  252. setIsDelTrue(flags, i); /* mark it as deleted in the old hash table */
  253. } else { /* write the element and jump out of the loop */
  254. _keys[i] = key;
  255. vals[i] = val;
  256. break;
  257. }
  258. }
  259. }
  260. }
  261. if (nBuckets > newNBuckets) /* shrink the hash table */
  262. {
  263. {
  264. var k = new NativeArray(newNBuckets);
  265. arrayCopy(_keys, 0, k, 0, newNBuckets);
  266. this._keys = k;
  267. }
  268. {
  269. var v = new NativeArray(newNBuckets);
  270. arrayCopy(vals, 0, v, 0, newNBuckets);
  271. this.vals = v;
  272. }
  273. }
  274. this.flags = newFlags;
  275. this.nBuckets = newNBuckets;
  276. this.nOccupied = size;
  277. this.upperBound = Std.int(newNBuckets * HASH_UPPER + .5);
  278. }
  279. }
  280. /**
  281. Returns an iterator of all keys in the hashtable.
  282. Implementation detail: Do not set() any new value while iterating, as it may cause a resize, which will break iteration
  283. **/
  284. public function keys() : Iterator<Int>
  285. {
  286. var i = 0;
  287. var len = nBuckets;
  288. return {
  289. hasNext: function() {
  290. for (j in i...len)
  291. {
  292. if (!isEither(flags, j))
  293. {
  294. i = j;
  295. return true;
  296. }
  297. }
  298. return false;
  299. },
  300. next: function() {
  301. var ret = _keys[i];
  302. cachedIndex = i;
  303. cachedKey = ret;
  304. i = i + 1;
  305. return ret;
  306. }
  307. };
  308. }
  309. /**
  310. Returns an iterator of all values in the hashtable.
  311. Implementation detail: Do not set() any new value while iterating, as it may cause a resize, which will break iteration
  312. **/
  313. public function iterator() : Iterator<T>
  314. {
  315. var i = 0;
  316. var len = nBuckets;
  317. return {
  318. hasNext: function() {
  319. for (j in i...len)
  320. {
  321. if (!isEither(flags, j))
  322. {
  323. i = j;
  324. return true;
  325. }
  326. }
  327. return false;
  328. },
  329. next: function() {
  330. var ret = vals[i];
  331. i = i + 1;
  332. return ret;
  333. }
  334. };
  335. }
  336. /**
  337. Returns an displayable representation of the hashtable content.
  338. **/
  339. public function toString() : String {
  340. var s = new StringBuf();
  341. s.add("{");
  342. var it = keys();
  343. for( i in it ) {
  344. s.add(i);
  345. s.add(" => ");
  346. s.add(Std.string(get(i)));
  347. if( it.hasNext() )
  348. s.add(", ");
  349. }
  350. s.add("}");
  351. return s.toString();
  352. }
  353. private static inline function assert(x:Bool):Void
  354. {
  355. #if debug
  356. if (!x) throw "assert failed";
  357. #end
  358. }
  359. private static inline function defaultK():Int return 0
  360. private static inline function arrayCopy(sourceArray:Dynamic, sourceIndex:Int, destinationArray:Dynamic, destinationIndex:Int, length:Int):Void
  361. java.lang.System.arraycopy(sourceArray, sourceIndex, destinationArray, destinationIndex, length)
  362. private static inline function getInc(k:Int, mask:Int):Int
  363. return (((k) >> 3 ^ (k) << 3) | 1) & (mask)
  364. private static inline function hash(i:Int):Int
  365. return i
  366. private static inline function flagIsEmpty(flag:NativeArray<Int>, i:Int):Bool
  367. return ( (flag[i >> 4] >>> ((i & 0xf) << 1)) & 2 ) != 0
  368. private static inline function flagIsDel(flag:NativeArray<Int>, i:Int):Bool
  369. return ((flag[i >> 4] >>> ((i & 0xf) << 1)) & 1) != 0
  370. private static inline function isEither(flag:NativeArray<Int>, i:Int):Bool
  371. return ((flag[i >> 4] >>> ((i & 0xf) << 1)) & 3) != 0
  372. private static inline function setIsDelFalse(flag:NativeArray<Int>, i:Int):Void
  373. flag[i >> 4] &= ~(1 << ((i & 0xf) << 1))
  374. private static inline function setIsEmptyFalse(flag:NativeArray<Int>, i:Int):Void
  375. flag[i >> 4] &= ~(2 << ((i & 0xf) << 1))
  376. private static inline function setIsBothFalse(flag:NativeArray<Int>, i:Int):Void
  377. flag[i >> 4] &= ~(3 << ((i & 0xf) << 1))
  378. private static inline function setIsDelTrue(flag:NativeArray<Int>, i:Int):Void
  379. flag[i >> 4] |= 1 << ((i & 0xf) << 1)
  380. private static inline function roundUp(x:Int):Int
  381. {
  382. --x;
  383. x |= (x) >>> 1;
  384. x |= (x) >>> 2;
  385. x |= (x) >>> 4;
  386. x |= (x) >>> 8;
  387. x |= (x) >>> 16;
  388. return ++x;
  389. }
  390. private static inline function flagsSize(m:Int):Int
  391. return ((m) < 16? 1 : (m) >> 4)
  392. }