IntHash.hx 11 KB

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