ObjectMap.hx 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561
  1. /*
  2. * Copyright (C)2005-2018 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 haxe.ds;
  23. import cs.NativeArray;
  24. @:coreApi class ObjectMap<K:{}, V> implements haxe.Constraints.IMap<K,V>
  25. {
  26. extern private static inline var HASH_UPPER = 0.77;
  27. extern private static inline var FLAG_EMPTY = 0;
  28. extern private static inline var FLAG_DEL = 1;
  29. /**
  30. * This is the most important structure here and the reason why it's so fast.
  31. * It's an array of all the hashes contained in the table. These hashes cannot be 0 nor 1,
  32. * which stand for "empty" and "deleted" states.
  33. *
  34. * The lookup algorithm will keep looking until a 0 or the key wanted is found;
  35. * The insertion algorithm will do the same but will also break when FLAG_DEL is found;
  36. */
  37. private var hashes:NativeArray<HashType>;
  38. private var _keys:NativeArray<K>;
  39. private var vals:NativeArray<V>;
  40. private var nBuckets:Int;
  41. private var size:Int;
  42. private var nOccupied:Int;
  43. private var upperBound:Int;
  44. #if !no_map_cache
  45. private var cachedKey:K;
  46. private var cachedIndex:Int;
  47. #end
  48. #if DEBUG_HASHTBL
  49. private var totalProbes:Int;
  50. private var probeTimes:Int;
  51. private var sameHash:Int;
  52. private var maxProbe:Int;
  53. #end
  54. public function new() : Void
  55. {
  56. #if !no_map_cache
  57. cachedIndex = -1;
  58. #end
  59. }
  60. public function set( key : K, value : V ) : Void
  61. {
  62. var x:Int, k:Int;
  63. if (nOccupied >= upperBound)
  64. {
  65. if (nBuckets > (size << 1))
  66. resize(nBuckets - 1); //clear "deleted" elements
  67. else
  68. resize(nBuckets + 2);
  69. }
  70. var hashes = hashes, keys = _keys, hashes = hashes;
  71. {
  72. var mask = (nBuckets == 0) ? 0 : nBuckets - 1;
  73. var site = x = nBuckets;
  74. k = hash(key);
  75. var i = k & mask, nProbes = 0;
  76. var delKey = -1;
  77. //for speed up
  78. if (isEmpty(hashes[i])) {
  79. x = i;
  80. } else {
  81. //var inc = getInc(k, mask);
  82. var last = i, flag;
  83. while(! (isEmpty(flag = hashes[i]) || (flag == k && _keys[i] == key)) )
  84. {
  85. if (isDel(flag) && delKey == -1)
  86. delKey = i;
  87. i = (i + ++nProbes) & mask;
  88. #if DEBUG_HASHTBL
  89. probeTimes++;
  90. if (i == last)
  91. throw "assert";
  92. #end
  93. }
  94. if (isEmpty(flag) && delKey != -1)
  95. x = delKey;
  96. else
  97. x = i;
  98. }
  99. #if DEBUG_HASHTBL
  100. if (nProbes > maxProbe)
  101. maxProbe = nProbes;
  102. totalProbes++;
  103. #end
  104. }
  105. var flag = hashes[x];
  106. if (isEmpty(flag))
  107. {
  108. keys[x] = key;
  109. vals[x] = value;
  110. hashes[x] = k;
  111. size++;
  112. nOccupied++;
  113. } else if (isDel(flag)) {
  114. keys[x] = key;
  115. vals[x] = value;
  116. hashes[x] = k;
  117. size++;
  118. } else {
  119. assert(_keys[x] == key);
  120. vals[x] = value;
  121. }
  122. #if !no_map_cache
  123. cachedIndex = x;
  124. cachedKey = key;
  125. #end
  126. }
  127. @:final private function lookup( key : K ) : Int
  128. {
  129. if (nBuckets != 0)
  130. {
  131. var hashes = hashes, keys = _keys;
  132. var mask = nBuckets - 1, hash = hash(key), k = hash, nProbes = 0;
  133. var i = k & mask;
  134. var last = i, flag;
  135. //var inc = getInc(k, mask);
  136. while (!isEmpty(flag = hashes[i]) && (isDel(flag) || flag != k || keys[i] != key))
  137. {
  138. i = (i + ++nProbes) & mask;
  139. #if DEBUG_HASHTBL
  140. probeTimes++;
  141. if (i == last)
  142. throw "assert";
  143. #end
  144. }
  145. #if DEBUG_HASHTBL
  146. if (nProbes > maxProbe)
  147. maxProbe = nProbes;
  148. totalProbes++;
  149. #end
  150. return isEither(flag) ? -1 : i;
  151. }
  152. return -1;
  153. }
  154. @:final function resize(newNBuckets:Int) : Void
  155. {
  156. //This function uses 0.25*n_bucktes bytes of working space instead of [sizeof(key_t+val_t)+.25]*n_buckets.
  157. var newHash = null;
  158. var j = 1;
  159. {
  160. newNBuckets = roundUp(newNBuckets);
  161. if (newNBuckets < 4) newNBuckets = 4;
  162. if (size >= (newNBuckets * HASH_UPPER + 0.5)) /* requested size is too small */
  163. {
  164. j = 0;
  165. } else { /* hash table size to be changed (shrink or expand); rehash */
  166. var nfSize = newNBuckets;
  167. newHash = new NativeArray( nfSize );
  168. if (nBuckets < newNBuckets) //expand
  169. {
  170. var k = new NativeArray(newNBuckets);
  171. if (_keys != null)
  172. arrayCopy(_keys, 0, k, 0, nBuckets);
  173. _keys = k;
  174. var v = new NativeArray(newNBuckets);
  175. if (vals != null)
  176. arrayCopy(vals, 0, v, 0, nBuckets);
  177. vals = v;
  178. } //otherwise shrink
  179. }
  180. }
  181. if (j != 0)
  182. { //rehashing is required
  183. #if !no_map_cache
  184. //resetting cache
  185. cachedKey = null;
  186. cachedIndex = -1;
  187. #end
  188. j = -1;
  189. var nBuckets = nBuckets, _keys = _keys, vals = vals, hashes = hashes;
  190. var newMask = newNBuckets - 1;
  191. while (++j < nBuckets)
  192. {
  193. var k;
  194. if (!isEither(k = hashes[j]))
  195. {
  196. var key = _keys[j];
  197. var val = vals[j];
  198. _keys[j] = null;
  199. vals[j] = cast null;
  200. hashes[j] = FLAG_DEL;
  201. while (true) /* kick-out process; sort of like in Cuckoo hashing */
  202. {
  203. var nProbes = 0;
  204. //var inc = getInc(k, newMask);
  205. var i = k & newMask;
  206. while (!isEmpty(newHash[i]))
  207. i = (i + ++nProbes) & newMask;
  208. newHash[i] = k;
  209. if (i < nBuckets && !isEither(k = hashes[i])) /* kick out the existing element */
  210. {
  211. {
  212. var tmp = _keys[i];
  213. _keys[i] = key;
  214. key = tmp;
  215. }
  216. {
  217. var tmp = vals[i];
  218. vals[i] = val;
  219. val = tmp;
  220. }
  221. hashes[i] = FLAG_DEL; /* mark it as deleted in the old hash table */
  222. } else { /* write the element and jump out of the loop */
  223. _keys[i] = key;
  224. vals[i] = val;
  225. break;
  226. }
  227. }
  228. }
  229. }
  230. if (nBuckets > newNBuckets) /* shrink the hash table */
  231. {
  232. {
  233. var k = new NativeArray(newNBuckets);
  234. arrayCopy(_keys, 0, k, 0, newNBuckets);
  235. this._keys = k;
  236. }
  237. {
  238. var v = new NativeArray(newNBuckets);
  239. arrayCopy(vals, 0, v, 0, newNBuckets);
  240. this.vals = v;
  241. }
  242. }
  243. this.hashes = newHash;
  244. this.nBuckets = newNBuckets;
  245. this.nOccupied = size;
  246. this.upperBound = Std.int(newNBuckets * HASH_UPPER + .5);
  247. }
  248. }
  249. public function get( key : K ) : Null<V>
  250. {
  251. var idx = -1;
  252. #if !no_map_cache
  253. if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
  254. {
  255. return vals[idx];
  256. }
  257. #end
  258. idx = lookup(key);
  259. if (idx != -1)
  260. {
  261. #if !no_map_cache
  262. cachedKey = key;
  263. cachedIndex = idx;
  264. #end
  265. return vals[idx];
  266. }
  267. return null;
  268. }
  269. private function getDefault( key : K, def : V ) : V
  270. {
  271. var idx = -1;
  272. #if !no_map_cache
  273. if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
  274. {
  275. return vals[idx];
  276. }
  277. #end
  278. idx = lookup(key);
  279. if (idx != -1)
  280. {
  281. #if !no_map_cache
  282. cachedKey = key;
  283. cachedIndex = idx;
  284. #end
  285. return vals[idx];
  286. }
  287. return def;
  288. }
  289. public function exists( key : K ) : Bool
  290. {
  291. var idx = -1;
  292. #if !no_map_cache
  293. if (cachedKey == key && ( (idx = cachedIndex) != -1 ))
  294. {
  295. return true;
  296. }
  297. #end
  298. idx = lookup(key);
  299. if (idx != -1)
  300. {
  301. #if !no_map_cache
  302. cachedKey = key;
  303. cachedIndex = idx;
  304. #end
  305. return true;
  306. }
  307. return false;
  308. }
  309. public function remove( key : K ) : Bool
  310. {
  311. var idx = -1;
  312. #if !no_map_cache
  313. if (! (cachedKey == key && ( (idx = cachedIndex) != -1 )))
  314. #end
  315. {
  316. idx = lookup(key);
  317. }
  318. if (idx == -1)
  319. {
  320. return false;
  321. } else {
  322. #if !no_map_cache
  323. if (cachedKey == key)
  324. cachedIndex = -1;
  325. #end
  326. hashes[idx] = FLAG_DEL;
  327. _keys[idx] = null;
  328. vals[idx] = null;
  329. --size;
  330. return true;
  331. }
  332. }
  333. /**
  334. Returns an iterator of all keys in the hashtable.
  335. Implementation detail: Do not set() any new value while iterating, as it may cause a resize, which will break iteration
  336. **/
  337. public function keys() : Iterator<K>
  338. {
  339. return new ObjectMapKeyIterator(this);
  340. }
  341. /**
  342. Returns an iterator of all values in the hashtable.
  343. Implementation detail: Do not set() any new value while iterating, as it may cause a resize, which will break iteration
  344. **/
  345. public function iterator() : Iterator<V>
  346. {
  347. return new ObjectMapValueIterator(this);
  348. }
  349. @:runtime public inline function keyValueIterator() : KeyValueIterator<K, V> {
  350. return new haxe.iterators.MapKeyValueIterator(this);
  351. }
  352. public function copy() : ObjectMap<K,V> {
  353. var copied = new ObjectMap<K, V>();
  354. for(key in keys()) copied.set(key, get(key));
  355. return copied;
  356. }
  357. /**
  358. Returns an displayable representation of the hashtable content.
  359. **/
  360. public function toString() : String {
  361. var s = new StringBuf();
  362. s.add("{");
  363. var it = keys();
  364. for( i in it ) {
  365. s.add(Std.string(i));
  366. s.add(" => ");
  367. s.add(Std.string(get(i)));
  368. if( it.hasNext() )
  369. s.add(", ");
  370. }
  371. s.add("}");
  372. return s.toString();
  373. }
  374. extern private static inline function roundUp(x:Int):Int
  375. {
  376. --x;
  377. x |= (x) >>> 1;
  378. x |= (x) >>> 2;
  379. x |= (x) >>> 4;
  380. x |= (x) >>> 8;
  381. x |= (x) >>> 16;
  382. return ++x;
  383. }
  384. extern private static inline function getInc(k:Int, mask:Int):Int //return 1 for linear probing
  385. return (((k) >> 3 ^ (k) << 3) | 1) & (mask);
  386. extern private static inline function isEither(v:HashType):Bool
  387. return (v & 0xFFFFFFFE) == 0;
  388. extern private static inline function isEmpty(v:HashType):Bool
  389. return v == FLAG_EMPTY;
  390. extern private static inline function isDel(v:HashType):Bool
  391. return v == FLAG_DEL;
  392. //guarantee: Whatever this function is, it will never return 0 nor 1
  393. extern private static inline function hash<K>(s:K):HashType
  394. {
  395. var k:Int = untyped s.GetHashCode();
  396. //k *= 357913941;
  397. //k ^= k << 24;
  398. //k += ~357913941;
  399. //k ^= k >> 31;
  400. //k ^= k << 31;
  401. k = (k+0x7ed55d16) + (k<<12);
  402. k = (k^0xc761c23c) ^ (k>>19);
  403. k = (k+0x165667b1) + (k<<5);
  404. k = (k+0xd3a2646c) ^ (k<<9);
  405. k = (k+0xfd7046c5) + (k<<3);
  406. k = (k^0xb55a4f09) ^ (k>>16);
  407. var ret = k;
  408. if (isEither(ret))
  409. {
  410. if (ret == 0)
  411. ret = 2;
  412. else
  413. ret = 0xFFFFFFFF;
  414. }
  415. return ret;
  416. }
  417. extern private static inline function arrayCopy(sourceArray:cs.system.Array, sourceIndex:Int, destinationArray:cs.system.Array, destinationIndex:Int, length:Int):Void
  418. cs.system.Array.Copy(sourceArray, sourceIndex, destinationArray, destinationIndex, length);
  419. extern private static inline function assert(x:Bool):Void
  420. {
  421. #if DEBUG_HASHTBL
  422. if (!x) throw "assert failed";
  423. #end
  424. }
  425. }
  426. @:access(haxe.ds.ObjectMap)
  427. @:final
  428. private class ObjectMapKeyIterator<T:{},V> {
  429. var m:ObjectMap<T,V>;
  430. var i:Int;
  431. var len:Int;
  432. public function new(m:ObjectMap<T,V>) {
  433. this.i = 0;
  434. this.m = m;
  435. this.len = m.nBuckets;
  436. }
  437. public function hasNext():Bool {
  438. for (j in i...len)
  439. {
  440. if (!ObjectMap.isEither(m.hashes[j]))
  441. {
  442. i = j;
  443. return true;
  444. }
  445. }
  446. return false;
  447. }
  448. public function next() : T {
  449. var ret = m._keys[i];
  450. #if !no_map_cache
  451. m.cachedIndex = i;
  452. m.cachedKey = ret;
  453. #end
  454. i = i + 1;
  455. return ret;
  456. }
  457. }
  458. @:access(haxe.ds.ObjectMap)
  459. @:final
  460. private class ObjectMapValueIterator<K:{},T> {
  461. var m:ObjectMap<K,T>;
  462. var i:Int;
  463. var len:Int;
  464. public function new(m:ObjectMap<K,T>)
  465. {
  466. this.i = 0;
  467. this.m = m;
  468. this.len = m.nBuckets;
  469. }
  470. public function hasNext() : Bool {
  471. for (j in i...len)
  472. {
  473. if (!ObjectMap.isEither(m.hashes[j]))
  474. {
  475. i = j;
  476. return true;
  477. }
  478. }
  479. return false;
  480. }
  481. public inline function next():T {
  482. var ret = m.vals[i];
  483. i = i + 1;
  484. return ret;
  485. }
  486. }
  487. private typedef HashType = Int;