ObjectMap.hx 12 KB

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