145 lines
4.9 KiB
JavaScript
145 lines
4.9 KiB
JavaScript
|
'use strict';
|
||
|
var dP = require('./_object-dp').f;
|
||
|
var create = require('./_object-create');
|
||
|
var redefineAll = require('./_redefine-all');
|
||
|
var ctx = require('./_ctx');
|
||
|
var anInstance = require('./_an-instance');
|
||
|
var forOf = require('./_for-of');
|
||
|
var $iterDefine = require('./_iter-define');
|
||
|
var step = require('./_iter-step');
|
||
|
var setSpecies = require('./_set-species');
|
||
|
var DESCRIPTORS = require('./_descriptors');
|
||
|
var fastKey = require('./_meta').fastKey;
|
||
|
var validate = require('./_validate-collection');
|
||
|
var SIZE = DESCRIPTORS ? '_s' : 'size';
|
||
|
|
||
|
var getEntry = function (that, key) {
|
||
|
// fast case
|
||
|
var index = fastKey(key);
|
||
|
var entry;
|
||
|
if (index !== 'F') return that._i[index];
|
||
|
// frozen object case
|
||
|
for (entry = that._f; entry; entry = entry.n) {
|
||
|
if (entry.k == key) return entry;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
module.exports = {
|
||
|
getConstructor: function (wrapper, NAME, IS_MAP, ADDER) {
|
||
|
var C = wrapper(function (that, iterable) {
|
||
|
anInstance(that, C, NAME, '_i');
|
||
|
that._t = NAME; // collection type
|
||
|
that._i = create(null); // index
|
||
|
that._f = undefined; // first entry
|
||
|
that._l = undefined; // last entry
|
||
|
that[SIZE] = 0; // size
|
||
|
if (iterable != undefined) forOf(iterable, IS_MAP, that[ADDER], that);
|
||
|
});
|
||
|
redefineAll(C.prototype, {
|
||
|
// 23.1.3.1 Map.prototype.clear()
|
||
|
// 23.2.3.2 Set.prototype.clear()
|
||
|
clear: function clear() {
|
||
|
for (var that = validate(this, NAME), data = that._i, entry = that._f; entry; entry = entry.n) {
|
||
|
entry.r = true;
|
||
|
if (entry.p) entry.p = entry.p.n = undefined;
|
||
|
delete data[entry.i];
|
||
|
}
|
||
|
that._f = that._l = undefined;
|
||
|
that[SIZE] = 0;
|
||
|
},
|
||
|
// 23.1.3.3 Map.prototype.delete(key)
|
||
|
// 23.2.3.4 Set.prototype.delete(value)
|
||
|
'delete': function (key) {
|
||
|
var that = validate(this, NAME);
|
||
|
var entry = getEntry(that, key);
|
||
|
if (entry) {
|
||
|
var next = entry.n;
|
||
|
var prev = entry.p;
|
||
|
delete that._i[entry.i];
|
||
|
entry.r = true;
|
||
|
if (prev) prev.n = next;
|
||
|
if (next) next.p = prev;
|
||
|
if (that._f == entry) that._f = next;
|
||
|
if (that._l == entry) that._l = prev;
|
||
|
that[SIZE]--;
|
||
|
} return !!entry;
|
||
|
},
|
||
|
// 23.2.3.6 Set.prototype.forEach(callbackfn, thisArg = undefined)
|
||
|
// 23.1.3.5 Map.prototype.forEach(callbackfn, thisArg = undefined)
|
||
|
forEach: function forEach(callbackfn /* , that = undefined */) {
|
||
|
validate(this, NAME);
|
||
|
var f = ctx(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3);
|
||
|
var entry;
|
||
|
while (entry = entry ? entry.n : this._f) {
|
||
|
f(entry.v, entry.k, this);
|
||
|
// revert to the last existing entry
|
||
|
while (entry && entry.r) entry = entry.p;
|
||
|
}
|
||
|
},
|
||
|
// 23.1.3.7 Map.prototype.has(key)
|
||
|
// 23.2.3.7 Set.prototype.has(value)
|
||
|
has: function has(key) {
|
||
|
return !!getEntry(validate(this, NAME), key);
|
||
|
}
|
||
|
});
|
||
|
if (DESCRIPTORS) dP(C.prototype, 'size', {
|
||
|
get: function () {
|
||
|
return validate(this, NAME)[SIZE];
|
||
|
}
|
||
|
});
|
||
|
return C;
|
||
|
},
|
||
|
def: function (that, key, value) {
|
||
|
var entry = getEntry(that, key);
|
||
|
var prev, index;
|
||
|
// change existing entry
|
||
|
if (entry) {
|
||
|
entry.v = value;
|
||
|
// create new entry
|
||
|
} else {
|
||
|
that._l = entry = {
|
||
|
i: index = fastKey(key, true), // <- index
|
||
|
k: key, // <- key
|
||
|
v: value, // <- value
|
||
|
p: prev = that._l, // <- previous entry
|
||
|
n: undefined, // <- next entry
|
||
|
r: false // <- removed
|
||
|
};
|
||
|
if (!that._f) that._f = entry;
|
||
|
if (prev) prev.n = entry;
|
||
|
that[SIZE]++;
|
||
|
// add to index
|
||
|
if (index !== 'F') that._i[index] = entry;
|
||
|
} return that;
|
||
|
},
|
||
|
getEntry: getEntry,
|
||
|
setStrong: function (C, NAME, IS_MAP) {
|
||
|
// add .keys, .values, .entries, [@@iterator]
|
||
|
// 23.1.3.4, 23.1.3.8, 23.1.3.11, 23.1.3.12, 23.2.3.5, 23.2.3.8, 23.2.3.10, 23.2.3.11
|
||
|
$iterDefine(C, NAME, function (iterated, kind) {
|
||
|
this._t = validate(iterated, NAME); // target
|
||
|
this._k = kind; // kind
|
||
|
this._l = undefined; // previous
|
||
|
}, function () {
|
||
|
var that = this;
|
||
|
var kind = that._k;
|
||
|
var entry = that._l;
|
||
|
// revert to the last existing entry
|
||
|
while (entry && entry.r) entry = entry.p;
|
||
|
// get next entry
|
||
|
if (!that._t || !(that._l = entry = entry ? entry.n : that._t._f)) {
|
||
|
// or finish the iteration
|
||
|
that._t = undefined;
|
||
|
return step(1);
|
||
|
}
|
||
|
// return step by kind
|
||
|
if (kind == 'keys') return step(0, entry.k);
|
||
|
if (kind == 'values') return step(0, entry.v);
|
||
|
return step(0, [entry.k, entry.v]);
|
||
|
}, IS_MAP ? 'entries' : 'values', !IS_MAP, true);
|
||
|
|
||
|
// add [@@species], 23.1.2.2, 23.2.2.2
|
||
|
setSpecies(NAME);
|
||
|
}
|
||
|
};
|