hash-map.js 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE below for additional
  4. * information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /******* NOTICE *********
  18. Apache Harmony
  19. Copyright 2006, 2010 The Apache Software Foundation.
  20. This product includes software developed at
  21. The Apache Software Foundation (http://www.apache.org/).
  22. Portions of Apache Harmony were originally developed by
  23. Intel Corporation and are licensed to the Apache Software
  24. Foundation under the "Software Grant and Corporate Contribution
  25. License Agreement" and for which the following copyright notices
  26. apply
  27. (C) Copyright 2005 Intel Corporation
  28. (C) Copyright 2005-2006 Intel Corporation
  29. (C) Copyright 2006 Intel Corporation
  30. The following copyright notice(s) were affixed to portions of the code
  31. with which this file is now or was at one time distributed
  32. and are placed here unaltered.
  33. (C) Copyright 1997,2004 International Business Machines Corporation.
  34. All rights reserved.
  35. (C) Copyright IBM Corp. 2003.
  36. This software contains code derived from UNIX V7, Copyright(C)
  37. Caldera International Inc.
  38. ************************/
  39. var referenceScore = 565;
  40. if (typeof (WScript) === "undefined") {
  41. var WScript = {
  42. Echo: print
  43. }
  44. }
  45. var print = function () {};
  46. var performance = performance || {};
  47. performance.now = (function() {
  48. return performance.now ||
  49. performance.mozNow ||
  50. performance.msNow ||
  51. performance.oNow ||
  52. performance.webkitNow ||
  53. Date.now;
  54. })();
  55. function reportResult(score) {
  56. WScript.Echo("### SCORE: " + (100 * referenceScore / score));
  57. }
  58. var top = {};
  59. top.JetStream = {
  60. goodTime: performance.now,
  61. reportResult: reportResult
  62. };
  63. var __time_before = top.JetStream.goodTime();
  64. ////////////////////////////////////////////////////////////////////////////////
  65. // Test
  66. ////////////////////////////////////////////////////////////////////////////////
  67. //@ runDefault
  68. // This code is a manual translation of Apache Harmony's HashMap class to
  69. // JavaScript.
  70. var HashMap = (function() {
  71. var DEFAULT_SIZE = 16;
  72. function calculateCapacity(x)
  73. {
  74. if (x >= 1 << 30)
  75. return 1 << 30;
  76. if (x == 0)
  77. return 16;
  78. x = x - 1;
  79. x |= x >> 1;
  80. x |= x >> 2;
  81. x |= x >> 4;
  82. x |= x >> 8;
  83. x |= x >> 16;
  84. return x + 1;
  85. }
  86. function computeHashCode(key)
  87. {
  88. switch (typeof key) {
  89. case "undefined":
  90. return 0;
  91. case "object":
  92. if (!key)
  93. return 0;
  94. case "function":
  95. return key.hashCode();
  96. case "boolean":
  97. return key | 0;
  98. case "number":
  99. if ((key | 0) == key)
  100. return key;
  101. key = "" + key; // Compute string hash of the double. It's the best we can do.
  102. case "string":
  103. // source: http://en.wikipedia.org/wiki/Java_hashCode()#Sample_implementations_of_the_java.lang.String_algorithm
  104. var h = 0;
  105. var len = key.length;
  106. for (var index = 0; index < len; index++) {
  107. h = (((31 * h) | 0) + key.charCodeAt(index)) | 0;
  108. }
  109. return h;
  110. default:
  111. throw new Error("Internal error: Bad JavaScript value type");
  112. }
  113. }
  114. function equals(a, b)
  115. {
  116. if (typeof a != typeof b)
  117. return false;
  118. switch (typeof a) {
  119. case "object":
  120. if (!a)
  121. return !b;
  122. case "function":
  123. switch (typeof b) {
  124. case "object":
  125. case "function":
  126. return a.equals(b);
  127. default:
  128. return false;
  129. }
  130. default:
  131. return a == b;
  132. }
  133. }
  134. function Entry(key, hash, value)
  135. {
  136. this._key = key;
  137. this._value = value;
  138. this._origKeyHash = hash;
  139. this._next = null;
  140. }
  141. Entry.prototype = {
  142. clone: function()
  143. {
  144. var result = new Entry(this._key, this._hash, this._value);
  145. if (this._next)
  146. result._next = this._next.clone();
  147. return result;
  148. },
  149. toString: function()
  150. {
  151. return this._key + "=" + this._value;
  152. },
  153. get key()
  154. {
  155. return this._key;
  156. },
  157. get value()
  158. {
  159. return this._value;
  160. }
  161. };
  162. function AbstractMapIterator(map)
  163. {
  164. this._associatedMap = map;
  165. this._expectedModCount = map._modCount;
  166. this._futureEntry = null;
  167. this._currentEntry = null;
  168. this._prevEntry = null;
  169. this._position = 0;
  170. }
  171. AbstractMapIterator.prototype = {
  172. hasNext: function()
  173. {
  174. if (this._futureEntry)
  175. return true;
  176. while (this._position < this._associatedMap._elementData.length) {
  177. if (!this._associatedMap._elementData[this._position])
  178. this._position++;
  179. else
  180. return true;
  181. }
  182. return false;
  183. },
  184. _checkConcurrentMod: function()
  185. {
  186. if (this._expectedModCount != this._associatedMap._modCount)
  187. throw new Error("Concurrent HashMap modification detected");
  188. },
  189. _makeNext: function()
  190. {
  191. this._checkConcurrentMod();
  192. if (!this.hasNext())
  193. throw new Error("No such element");
  194. if (!this._futureEntry) {
  195. this._currentEntry = this._associatedMap._elementData[this._position++];
  196. this._futureEntry = this._currentEntry._next;
  197. this._prevEntry = null;
  198. return;
  199. }
  200. if (this._currentEntry)
  201. this._prevEntry = this._currentEntry;
  202. this._currentEntry = this._futureEntry;
  203. this._futureEntry = this._futureEntry._next;
  204. },
  205. remove: function()
  206. {
  207. this._checkConcurrentMod();
  208. if (!this._currentEntry)
  209. throw new Error("Illegal state");
  210. if (!this._prevEntry) {
  211. var index = this._currentEntry._origKeyHash & (this._associatedMap._elementData.length - 1);
  212. this._associatedMap._elementData[index] = this._associatedMap._elementData[index]._next;
  213. } else
  214. this._prevEntry._next = this._currentEntry._next;
  215. this._currentEntry = null;
  216. this._expectedModCount++;
  217. this._associatedMap._modCount++;
  218. this._associatedMap._elementCount--;
  219. }
  220. };
  221. function EntryIterator(map)
  222. {
  223. AbstractMapIterator.call(this, map);
  224. }
  225. EntryIterator.prototype = {
  226. next: function()
  227. {
  228. this._makeNext();
  229. return this._currentEntry;
  230. }
  231. };
  232. EntryIterator.prototype.__proto__ = AbstractMapIterator.prototype;
  233. function KeyIterator(map)
  234. {
  235. AbstractMapIterator.call(this, map);
  236. }
  237. KeyIterator.prototype = {
  238. next: function()
  239. {
  240. this.makeNext();
  241. return this._currentEntry._key;
  242. }
  243. };
  244. KeyIterator.prototype.__proto__ = AbstractMapIterator.prototype;
  245. function ValueIterator(map)
  246. {
  247. AbstractMapIterator.call(this, map);
  248. }
  249. ValueIterator.prototype = {
  250. next: function()
  251. {
  252. this.makeNext();
  253. return this._currentEntry._value;
  254. }
  255. };
  256. ValueIterator.prototype.__proto__ = AbstractMapIterator.prototype;
  257. function EntrySet(map)
  258. {
  259. this._associatedMap = map;
  260. }
  261. EntrySet.prototype = {
  262. size: function()
  263. {
  264. return this._associatedMap._elementCount;
  265. },
  266. clear: function()
  267. {
  268. this._associatedMap.clear();
  269. },
  270. remove: function(object)
  271. {
  272. var entry = this._associatedMap._getEntry(object.key);
  273. if (!entry)
  274. return false;
  275. if (!equals(entry._value, object.value))
  276. return false;
  277. this._associatedMap._removeEntry(entry);
  278. return true;
  279. },
  280. contains: function(object)
  281. {
  282. var entry = this._associatedMap._getEntry(object.key);
  283. if (!entry)
  284. return false;
  285. return equals(entry._value, object.value);
  286. },
  287. iterator: function()
  288. {
  289. return new EntryIterator(this._associatedMap);
  290. }
  291. };
  292. function KeySet(map)
  293. {
  294. this._associatedMap = map;
  295. }
  296. KeySet.prototype = {
  297. contains: function(object)
  298. {
  299. return this._associatedMap.contains(object);
  300. },
  301. size: function()
  302. {
  303. return this._associatedMap._elementCount;
  304. },
  305. clear: function()
  306. {
  307. this._associatedMap.clear();
  308. },
  309. remove: function(key)
  310. {
  311. return !!this._associatedMap.remove(key);
  312. },
  313. iterator: function()
  314. {
  315. return new KeyIterator(this._associatedMap);
  316. }
  317. };
  318. function ValueCollection(map)
  319. {
  320. this._associatedMap = map;
  321. }
  322. ValueCollection.prototype = {
  323. contains: function(object)
  324. {
  325. return this._associatedMap.containsValue(object);
  326. },
  327. size: function()
  328. {
  329. return this._associatedMap._elementCount;
  330. },
  331. clear: function()
  332. {
  333. this._associatedMap.clear();
  334. },
  335. iterator: function()
  336. {
  337. return new ValueIterator(this._associatedMap);
  338. }
  339. };
  340. function HashMap(capacity, loadFactor)
  341. {
  342. if (capacity == null)
  343. capacity = DEFAULT_SIZE;
  344. if (loadFactor == null)
  345. loadFactor = 0.75;
  346. if (capacity < 0)
  347. throw new Error("Invalid argument to HashMap constructor: capacity is negative");
  348. if (loadFactor <= 0)
  349. throw new Error("Invalid argument to HashMap constructor: loadFactor is not positive");
  350. this._capacity = calculateCapacity(capacity);
  351. this._elementCount = 0;
  352. this._elementData = new Array(this.capacity);
  353. this._loadFactor = loadFactor;
  354. this._modCount = 0;
  355. this._computeThreshold();
  356. }
  357. HashMap.prototype = {
  358. _computeThreshold: function()
  359. {
  360. this._threshold = (this._elementData.length * this._loadFactor) | 0;
  361. },
  362. clear: function()
  363. {
  364. if (!this._elementCount)
  365. return;
  366. this._elementCount = 0;
  367. for (var i = this._elementData.length; i--;)
  368. this._elementData[i] = null;
  369. this._modCount++;
  370. },
  371. clone: function()
  372. {
  373. var result = new HashMap(this._elementData.length, this._loadFactor);
  374. result.putAll(this);
  375. return result;
  376. },
  377. containsKey: function(key)
  378. {
  379. return !!this._getEntry(key);
  380. },
  381. containsValue: function(value)
  382. {
  383. for (var i = this._elementData.length; i--;) {
  384. for (var entry = this._elementData[i]; entry; entry = entry._next) {
  385. if (equals(value, entry._value))
  386. return true;
  387. }
  388. }
  389. return false;
  390. },
  391. entrySet: function()
  392. {
  393. return new EntrySet(this);
  394. },
  395. get: function(key)
  396. {
  397. var entry = this._getEntry(key);
  398. if (entry)
  399. return entry._value;
  400. return null;
  401. },
  402. _getEntry: function(key)
  403. {
  404. var hash = computeHashCode(key);
  405. var index = hash & (this._elementData.length - 1);
  406. return this._findKeyEntry(key, index, hash);
  407. },
  408. _findKeyEntry: function(key, index, keyHash)
  409. {
  410. var entry = this._elementData[index];
  411. while (entry && (entry._origKeyHash != keyHash || !equals(key, entry._key)))
  412. entry = entry._next;
  413. return entry;
  414. },
  415. isEmpty: function()
  416. {
  417. return !this._elementCount;
  418. },
  419. keySet: function()
  420. {
  421. return new KeySet(this);
  422. },
  423. put: function(key, value)
  424. {
  425. var hash = computeHashCode(key);
  426. var index = hash & (this._elementData.length - 1);
  427. var entry = this._findKeyEntry(key, index, hash);
  428. if (!entry) {
  429. this._modCount++;
  430. entry = this._createHashedEntry(key, index, hash);
  431. if (++this._elementCount > this._threshold)
  432. this._rehash();
  433. }
  434. var result = entry._value;
  435. entry._value = value;
  436. return result;
  437. },
  438. _createHashedEntry: function(key, index, hash)
  439. {
  440. var entry = new Entry(key, hash, null);
  441. entry._next = this._elementData[index];
  442. this._elementData[index] = entry;
  443. return entry;
  444. },
  445. putAll: function(map)
  446. {
  447. if (map.isEmpty())
  448. return;
  449. for (var iter = map.entrySet().iterator(); iter.hasNext();) {
  450. var entry = iter.next();
  451. put(entry.key, entry.value);
  452. }
  453. },
  454. _rehash: function(capacity)
  455. {
  456. if (capacity == null)
  457. capacity = this._elementData.length;
  458. var length = calculateCapacity(!capacity ? 1 : capacity << 1);
  459. var newData = new Array(length);
  460. for (var i = 0; i < this._elementData.length; ++i) {
  461. var entry = this._elementData[i];
  462. this._elementData[i] = null;
  463. while (entry) {
  464. var index = entry._origKeyHash & (length - 1);
  465. var next = entry._next;
  466. entry._next = newData[index];
  467. newData[index] = entry;
  468. entry = next;
  469. }
  470. }
  471. this._elementData = newData;
  472. this._computeThreshold();
  473. },
  474. remove: function(key)
  475. {
  476. var entry = this._removeEntryForKey(key);
  477. if (!entry)
  478. return null;
  479. return entry._value;
  480. },
  481. _removeEntry: function(entry)
  482. {
  483. var index = entry._origKeyHash & (this._elementData.length - 1);
  484. var current = this._elementData[index];
  485. if (current == entry)
  486. this._elementData[index] = entry._next;
  487. else {
  488. while (current._next != entry)
  489. current = current._next;
  490. current._next = entry._next;
  491. }
  492. this._modCount++;
  493. this._elementCount--;
  494. },
  495. _removeEntryForKey: function(key)
  496. {
  497. var hash = computeHashCode(key);
  498. var index = hash & (this._elementData.length - 1);
  499. var entry = this._elementData[index];
  500. var last = null;
  501. while (entry != null && !(entry._origKeyHash == hash && equals(key, entry._key))) {
  502. last = entry;
  503. entry = entry._next;
  504. }
  505. if (!entry)
  506. return null;
  507. if (!last)
  508. this._elementData[index] = entry._next;
  509. else
  510. last._next = entry._next;
  511. this._modCount++;
  512. this._elementCount--;
  513. return entry;
  514. },
  515. size: function()
  516. {
  517. return this._elementCount;
  518. },
  519. values: function()
  520. {
  521. return new ValueCollection(this);
  522. }
  523. };
  524. return HashMap;
  525. })();
  526. var map = new HashMap();
  527. var COUNT = 500000;
  528. for (var i = 0; i < COUNT; ++i)
  529. map.put(i, 42);
  530. var result = 0;
  531. for (var j = 0; j < 5; ++j) {
  532. for (var i = 0; i < COUNT; ++i)
  533. result += map.get(i);
  534. }
  535. var keySum = 0;
  536. var valueSum = 0;
  537. for (var iterator = map.entrySet().iterator(); iterator.hasNext();) {
  538. var entry = iterator.next();
  539. keySum += entry.key;
  540. valueSum += entry.value;
  541. }
  542. if (result != 105000000)
  543. throw "Error: result = " + result;
  544. if (keySum != 124999750000)
  545. throw "Error: keySum = " + keySum;
  546. if (valueSum != 21000000)
  547. throw "Error: valueSum = " + valueSum;
  548. ////////////////////////////////////////////////////////////////////////////////
  549. // Runner
  550. ////////////////////////////////////////////////////////////////////////////////
  551. var __time_after = top.JetStream.goodTime();
  552. top.JetStream.reportResult(__time_after - __time_before);