/* */ 'use strict'; var $ = require("./$"), ctx = require("./$.ctx"), safe = require("./$.uid").safe, assert = require("./$.assert"), forOf = require("./$.for-of"), step = require("./$.iter").step, $has = $.has, set = $.set, isObject = $.isObject, hide = $.hide, isExtensible = Object.isExtensible || isObject, ID = safe('id'), O1 = safe('O1'), LAST = safe('last'), FIRST = safe('first'), ITER = safe('iter'), SIZE = $.DESC ? safe('size') : 'size', id = 0; function fastKey(it, create) { if (!isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it; if (!$has(it, ID)) { if (!isExtensible(it)) return 'F'; if (!create) return 'E'; hide(it, ID, ++id); } return 'O' + it[ID]; } function getEntry(that, key) { var index = fastKey(key), entry; if (index !== 'F') return that[O1][index]; for (entry = that[FIRST]; 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) { assert.inst(that, C, NAME); set(that, O1, $.create(null)); set(that, SIZE, 0); set(that, LAST, undefined); set(that, FIRST, undefined); if (iterable != undefined) forOf(iterable, IS_MAP, that[ADDER], that); }); require("./$.mix")(C.prototype, { clear: function clear() { for (var that = this, data = that[O1], entry = that[FIRST]; entry; entry = entry.n) { entry.r = true; if (entry.p) entry.p = entry.p.n = undefined; delete data[entry.i]; } that[FIRST] = that[LAST] = undefined; that[SIZE] = 0; }, 'delete': function(key) { var that = this, entry = getEntry(that, key); if (entry) { var next = entry.n, prev = entry.p; delete that[O1][entry.i]; entry.r = true; if (prev) prev.n = next; if (next) next.p = prev; if (that[FIRST] == entry) that[FIRST] = next; if (that[LAST] == entry) that[LAST] = prev; that[SIZE]--; } return !!entry; }, forEach: function forEach(callbackfn) { var f = ctx(callbackfn, arguments[1], 3), entry; while (entry = entry ? entry.n : this[FIRST]) { f(entry.v, entry.k, this); while (entry && entry.r) entry = entry.p; } }, has: function has(key) { return !!getEntry(this, key); } }); if ($.DESC) $.setDesc(C.prototype, 'size', {get: function() { return assert.def(this[SIZE]); }}); return C; }, def: function(that, key, value) { var entry = getEntry(that, key), prev, index; if (entry) { entry.v = value; } else { that[LAST] = entry = { i: index = fastKey(key, true), k: key, v: value, p: prev = that[LAST], n: undefined, r: false }; if (!that[FIRST]) that[FIRST] = entry; if (prev) prev.n = entry; that[SIZE]++; if (index !== 'F') that[O1][index] = entry; } return that; }, getEntry: getEntry, setIter: function(C, NAME, IS_MAP) { require("./$.iter-define")(C, NAME, function(iterated, kind) { set(this, ITER, { o: iterated, k: kind }); }, function() { var iter = this[ITER], kind = iter.k, entry = iter.l; while (entry && entry.r) entry = entry.p; if (!iter.o || !(iter.l = entry = entry ? entry.n : iter.o[FIRST])) { iter.o = undefined; return step(1); } 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); } };