Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(54)

Unified Diff: src/collection.js

Issue 947683002: Reimplement Maps and Sets in JS (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Moved more to JS Created 5 years, 9 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | src/hydrogen.h » ('j') | src/runtime/runtime-collections.cc » ('J')
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/collection.js
diff --git a/src/collection.js b/src/collection.js
index c8cf639f97d96c3396f463a97ad5181ac4ccc347..1fe083c2a8c450574e384c7f8c2c0f3dd8df9938 100644
--- a/src/collection.js
+++ b/src/collection.js
@@ -12,6 +12,87 @@ var $Set = global.Set;
var $Map = global.Map;
+(function() {
+ "use strict";
+
+ function HashToBucket(hash, numBuckets) {
+ return hash & (numBuckets - 1);
+ }
+ %SetInlineBuiltinFlag(HashToBucket);
+
+
+ function HashToEntry(table, hash, numBuckets) {
+ var bucket = HashToBucket(hash, numBuckets);
+ return %_FixedArrayGet(table, HASH_TABLE_START_INDEX + bucket);
+ }
+ %SetInlineBuiltinFlag(HashToEntry);
+
+
+ function SetFindEntry(table, numBuckets, key, hash) {
+ var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key);
+ for (var entry = HashToEntry(table, hash, numBuckets);
+ entry !== NOT_FOUND;
+ entry = SET_CHAIN_AT(table, entry, numBuckets)) {
+ var candidate = SET_KEY_AT(table, entry, numBuckets);
+ if (key === candidate) {
+ return entry;
+ }
+ if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) {
+ return entry;
+ }
+ }
+ return NOT_FOUND;
+ }
+ %SetInlineBuiltinFlag(SetFindEntry);
+
+
+ function MapFindEntry(table, numBuckets, key, hash) {
+ var keyIsNaN = IS_NUMBER(key) && NUMBER_IS_NAN(key);
+ for (var entry = HashToEntry(table, hash, numBuckets);
+ entry !== NOT_FOUND;
+ entry = MAP_CHAIN_AT(table, entry, numBuckets)) {
+ var candidate = MAP_KEY_AT(table, entry, numBuckets);
+ if (key === candidate) {
+ return entry;
+ }
+ if (keyIsNaN && IS_NUMBER(candidate) && NUMBER_IS_NAN(candidate)) {
+ return entry;
+ }
+ }
+ return NOT_FOUND;
+ }
+ %SetInlineBuiltinFlag(MapFindEntry);
+
+
+ function ComputeIntegerHash(key, seed) {
+ var hash = key;
+ hash = hash ^ seed;
+ hash = ~hash + (hash << 15); // hash = (hash << 15) - hash - 1;
+ hash = hash ^ (hash >>> 12);
+ hash = hash + (hash << 2);
+ hash = hash ^ (hash >>> 4);
+ hash = (hash * 2057) | 0; // hash = (hash + (hash << 3)) + (hash << 11);
+ hash = hash ^ (hash >>> 16);
+ return hash;
+ }
+ %SetInlineBuiltinFlag(ComputeIntegerHash);
+
+
+ function GetHash(key) {
+ if (%_IsSmi(key)) {
+ return ComputeIntegerHash(key, 0);
+ }
+ if (IS_STRING(key)) {
+ var field = %_StringGetRawHashField(key);
+ if ((field & HASH_NOT_COMPUTED_MASK) === 0) {
+ return (field >>> STRING_HASH_SHIFT);
+ }
+ }
+ return %GenericHash(key);
+ }
+ %SetInlineBuiltinFlag(GetHash);
+
+
// -------------------------------------------------------------------
// Harmony Set
@@ -35,7 +116,7 @@ function SetConstructor(iterable) {
}
-function SetAddJS(key) {
+function SetAdd(key) {
if (!IS_SET(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.add', this]);
@@ -44,37 +125,76 @@ function SetAddJS(key) {
// Even though we use SameValueZero as the comparison for the keys we don't
// want to ever store -0 as the key since the key is directly exposed when
// doing iteration.
- if (key === 0) {
+ if (IS_NUMBER(key) && key === 0) {
key = 0;
}
- return %_SetAdd(this, key);
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX);
+ var hash = GetHash(key);
+ if (SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND) return this;
+
+ var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX);
+ var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX);
+ var capacity = numBuckets << 1;
+ if ((nof + nod) >= capacity) {
+ // Need to grow, bail out to runtime.
+ %SetGrow(this);
+ // Re-enter...TODO(adamk) Fix this
+ return %_CallFunction(this, key, SetAdd);
+ }
+ var entry = nof + nod;
+ var index = SET_ENTRY_TO_INDEX(entry, numBuckets);
+ var bucket = HashToBucket(hash, numBuckets);
+ var chainEntry = %_FixedArrayGet(table, HASH_TABLE_START_INDEX + bucket);
+ %_FixedArraySet(table, HASH_TABLE_START_INDEX + bucket, entry | 0);
+ %_FixedArraySet(table, index + SET_CHAIN_OFFSET, chainEntry | 0);
+ %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, (nof + 1) | 0);
+ %_FixedArraySet(table, index, key);
+ return this;
}
-function SetHasJS(key) {
+function SetHas(key) {
if (!IS_SET(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.has', this]);
}
- return %_SetHas(this, key);
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX);
+ var hash = GetHash(key);
+ return SetFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
}
-function SetDeleteJS(key) {
+function SetDelete(key) {
if (!IS_SET(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.delete', this]);
}
- return %_SetDelete(this, key);
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX);
+ var hash = GetHash(key);
+ var entry = SetFindEntry(table, numBuckets, key, hash);
+ if (entry === NOT_FOUND) return false;
+
+ var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX) - 1;
+ var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX) + 1;
+ var index = SET_ENTRY_TO_INDEX(entry, numBuckets);
+ %_FixedArraySet(table, index, %_TheHole());
+ %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof | 0);
+ %_FixedArraySet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX, nod | 0);
+ if (nof < (numBuckets >>> 1)) %SetShrink(this);
+ return true;
}
-function SetGetSizeJS() {
+function SetGetSize() {
if (!IS_SET(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Set.prototype.size', this]);
}
- return %_SetGetSize(this);
+ var table = %_JSCollectionGetTable(this);
+ return %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX);
}
@@ -130,11 +250,11 @@ function SetUpSet() {
%FunctionSetLength(SetForEach, 1);
// Set up the non-enumerable functions on the Set prototype object.
- InstallGetter($Set.prototype, "size", SetGetSizeJS);
+ InstallGetter($Set.prototype, "size", SetGetSize);
InstallFunctions($Set.prototype, DONT_ENUM, $Array(
- "add", SetAddJS,
- "has", SetHasJS,
- "delete", SetDeleteJS,
+ "add", SetAdd,
+ "has", SetHas,
+ "delete", SetDelete,
"clear", SetClearJS,
"forEach", SetForEach
));
@@ -169,16 +289,21 @@ function MapConstructor(iterable) {
}
-function MapGetJS(key) {
+function MapGet(key) {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.get', this]);
}
- return %_MapGet(this, key);
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX);
+ var hash = GetHash(key);
+ var entry = MapFindEntry(table, numBuckets, key, hash);
+ if (entry === NOT_FOUND) return UNDEFINED;
+ return MAP_VALUE_AT(table, entry, numBuckets);
}
-function MapSetJS(key, value) {
+function MapSet(key, value) {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.set', this]);
@@ -187,37 +312,84 @@ function MapSetJS(key, value) {
// Even though we use SameValueZero as the comparison for the keys we don't
// want to ever store -0 as the key since the key is directly exposed when
// doing iteration.
- if (key === 0) {
+ if (IS_NUMBER(key) && key === 0) {
key = 0;
}
- return %_MapSet(this, key, value);
+
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX);
+ var hash = GetHash(key);
+ var entry = MapFindEntry(table, numBuckets, key, hash);
+ if (entry !== NOT_FOUND) {
+ var existingIndex = MAP_ENTRY_TO_INDEX(entry, numBuckets);
+ %_FixedArraySet(table, existingIndex + 1, value);
+ return this;
+ }
+
+ var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX);
+ var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX);
+ var capacity = numBuckets << 1;
+ if ((nof + nod) >= capacity) {
+ // Need to grow, bail out to runtime.
+ %MapGrow(this);
+ // Re-enter...TODO(adamk) Fix this
+ return %_CallFunction(this, key, value, MapSet);
+ }
+ entry = nof + nod;
+ var index = MAP_ENTRY_TO_INDEX(entry, numBuckets);
+ var bucket = HashToBucket(hash, numBuckets);
+ var chainEntry = %_FixedArrayGet(table, HASH_TABLE_START_INDEX + bucket);
+ %_FixedArraySet(table, HASH_TABLE_START_INDEX + bucket, entry | 0);
+ %_FixedArraySet(table, index + MAP_CHAIN_OFFSET, chainEntry | 0);
+ %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, (nof + 1) | 0);
+ %_FixedArraySet(table, index, key);
+ %_FixedArraySet(table, index + 1, value);
+ return this;
}
-function MapHasJS(key) {
+function MapHas(key) {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.has', this]);
}
- return %_MapHas(this, key);
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX);
+ var hash = GetHash(key);
+ return MapFindEntry(table, numBuckets, key, hash) !== NOT_FOUND;
}
-function MapDeleteJS(key) {
+function MapDelete(key) {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.delete', this]);
}
- return %_MapDelete(this, key);
+ var table = %_JSCollectionGetTable(this);
+ var numBuckets = %_FixedArrayGet(table, NUMBER_OF_BUCKETS_INDEX);
+ var hash = GetHash(key);
+ var entry = MapFindEntry(table, numBuckets, key, hash);
+ if (entry === NOT_FOUND) return false;
+
+ var nof = %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX) - 1;
+ var nod = %_FixedArrayGet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX) + 1;
+ var index = MAP_ENTRY_TO_INDEX(entry, numBuckets);
+ %_FixedArraySet(table, index, %_TheHole());
+ %_FixedArraySet(table, index + 1, %_TheHole());
+ %_FixedArraySet(table, NUMBER_OF_ELEMENTS_INDEX, nof | 0);
+ %_FixedArraySet(table, NUMBER_OF_DELETED_ELEMENTS_INDEX, nod | 0);
+ if (nof < (numBuckets >>> 1)) %MapShrink(this);
+ return true;
}
-function MapGetSizeJS() {
+function MapGetSize() {
if (!IS_MAP(this)) {
throw MakeTypeError('incompatible_method_receiver',
['Map.prototype.size', this]);
}
- return %_MapGetSize(this);
+ var table = %_JSCollectionGetTable(this);
+ return %_FixedArrayGet(table, NUMBER_OF_ELEMENTS_INDEX);
}
@@ -271,15 +443,17 @@ function SetUpMap() {
%FunctionSetLength(MapForEach, 1);
// Set up the non-enumerable functions on the Map prototype object.
- InstallGetter($Map.prototype, "size", MapGetSizeJS);
+ InstallGetter($Map.prototype, "size", MapGetSize);
InstallFunctions($Map.prototype, DONT_ENUM, $Array(
- "get", MapGetJS,
- "set", MapSetJS,
- "has", MapHasJS,
- "delete", MapDeleteJS,
+ "get", MapGet,
+ "set", MapSet,
+ "has", MapHas,
+ "delete", MapDelete,
"clear", MapClearJS,
"forEach", MapForEach
));
}
SetUpMap();
+
+})();
« no previous file with comments | « no previous file | src/hydrogen.h » ('j') | src/runtime/runtime-collections.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698