UNPKG

@rimbu/hashed

Version:

Immutable HashMap and HashSet implementations for TypeScript

441 lines 18.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.HashMapCollisionBuilder = exports.HashMapBlockBuilder = void 0; var tslib_1 = require("tslib"); var base_1 = require("@rimbu/base"); var common_1 = require("@rimbu/common"); var list_1 = require("@rimbu/list"); var stream_1 = require("@rimbu/stream"); var custom_1 = require("@rimbu/stream/custom"); var common_2 = require("@rimbu/hashed/common"); var HashMapBlockBuilder = /** @class */ (function (_super) { tslib_1.__extends(HashMapBlockBuilder, _super); function HashMapBlockBuilder(context, source, _entries, _entrySets, size, level) { if (size === void 0) { size = (_a = source === null || source === void 0 ? void 0 : source.size) !== null && _a !== void 0 ? _a : 0; } if (level === void 0) { level = (_b = source === null || source === void 0 ? void 0 : source.level) !== null && _b !== void 0 ? _b : 0; } var _a, _b; var _this = _super.call(this) || this; _this.context = context; _this.source = source; _this._entries = _entries; _this._entrySets = _entrySets; _this.size = size; _this.level = level; _this._lock = 0; _this.get = function (key, otherwise, hash) { if (undefined !== _this.source) return _this.source.get(key, otherwise); if (!_this.context.hasher.isValid(key)) return (0, common_1.OptLazy)(otherwise); var keyHash = hash !== null && hash !== void 0 ? hash : _this.context.hash(key); var keyIndex = _this.context.getKeyIndex(_this.level, keyHash); if (keyIndex in _this.entries) { var currentEntry = _this.entries[keyIndex]; if (_this.context.eq(key, currentEntry[0])) return currentEntry[1]; return (0, common_1.OptLazy)(otherwise); } if (keyIndex in _this.entrySets) { var currentEntrySet = _this.entrySets[keyIndex]; return currentEntrySet.get(key, otherwise, keyHash); } return (0, common_1.OptLazy)(otherwise); }; // prettier-ignore _this.hasKey = function (key) { var token = Symbol(); return token !== _this.get(key, token); }; _this.addEntry = function (entry) { _this.checkLock(); return _this.addEntryInternal(entry); }; _this.addEntries = function (source) { _this.checkLock(); if ((0, custom_1.isEmptyStreamSourceInstance)(source)) return false; return stream_1.Stream.from(source).filterPure({ pred: _this.addEntry }).count() > 0; }; _this.set = function (key, value) { _this.checkLock(); return _this.addEntryInternal([key, value]); }; _this.modifyAt = function (key, options, keyHash) { if (keyHash === void 0) { keyHash = _this.context.hash(key); } _this.checkLock(); var keyIndex = _this.context.getKeyIndex(_this.level, keyHash); if (keyIndex in _this.entries) { // potential match in entries var currentEntry = _this.entries[keyIndex]; var _a = tslib_1.__read(currentEntry, 2), currentKey = _a[0], currentValue = _a[1]; if (_this.context.eq(key, currentKey)) { // exact match if (undefined === options.ifExists) return false; var newValue_1 = options.ifExists instanceof Function ? options.ifExists(currentValue, base_1.Token) : options.ifExists; if (Object.is(newValue_1, currentValue)) { return false; } _this.source = undefined; if (base_1.Token === newValue_1) { _this.size--; delete _this.entries[keyIndex]; return true; } // replace current value var newEntry = [key, newValue_1]; _this.entries[keyIndex] = newEntry; return true; } if (undefined === options.ifNew) return false; // no match, replace entry with entryset containing both entries var newValue_2 = (0, common_1.OptLazyOr)(options.ifNew, base_1.Token); if (base_1.Token === newValue_2) return false; _this.source = undefined; _this.size++; delete _this.entries[keyIndex]; var newEntrySet = _this.level < _this.context.maxDepth ? new HashMapBlockBuilder(_this.context, undefined, undefined, undefined, 0, _this.level + 1) : new HashMapCollisionBuilder(_this.context); newEntrySet.addEntryInternal(currentEntry); newEntrySet.addEntryInternal([key, newValue_2], keyHash); _this.entrySets[keyIndex] = newEntrySet; return true; } if (keyIndex in _this.entrySets) { // potential match in entrysets var entrySet = _this.entrySets[keyIndex]; var preSize = entrySet.size; var result = entrySet.modifyAt(key, options, keyHash); if (result) _this.source = undefined; _this.size += entrySet.size - preSize; if (entrySet.size > 1) return result; // single entry needs to be pulled up var first = undefined; if (_this.context.isHashMapBlockBuilder(entrySet)) { for (var index in entrySet.entries) { first = entrySet.entries[index]; break; } } else { first = entrySet.entries.get(0, base_1.RimbuError.throwInvalidStateError); } delete _this.entrySets[keyIndex]; _this.entries[keyIndex] = first; return true; } if (undefined === options.ifNew) return false; // no matching entry or entrySet var newValue = (0, common_1.OptLazyOr)(options.ifNew, base_1.Token); if (base_1.Token === newValue) return false; _this.source = undefined; _this.size++; _this.entries[keyIndex] = [key, newValue]; return true; }; // prettier-ignore _this.updateAt = function (key, update, otherwise) { var result; var found = false; _this.modifyAt(key, { ifExists: function (value) { result = value; found = true; return (0, common_1.Update)(value, update); }, }); if (!found) return (0, common_1.OptLazy)(otherwise); return result; }; _this.removeKey = function (key, otherwise) { _this.checkLock(); if (!_this.context.hasher.isValid(key)) return (0, common_1.OptLazy)(otherwise); var removedValue; var found = false; _this.modifyAt(key, { ifExists: function (currentValue, remove) { removedValue = currentValue; found = true; return remove; }, }); if (!found) return (0, common_1.OptLazy)(otherwise); return removedValue; }; // prettier-ignore _this.removeKeys = function (keys) { _this.checkLock(); if ((0, custom_1.isEmptyStreamSourceInstance)(keys)) return false; var notFound = Symbol(); return (stream_1.Stream.from(keys) .mapPure(_this.removeKey, notFound) .countElement(notFound, { negate: true }) > 0); }; _this.forEach = function (f, options) { if (options === void 0) { options = {}; } var _a = options.state, state = _a === void 0 ? (0, common_1.TraverseState)() : _a; _this._lock++; _super.prototype.forEach.call(_this, f, { state: state }); _this._lock--; }; _this.build = function () { if (_this.size === 0) return _this.context.empty(); return _this.buildNE(); }; // prettier-ignore _this.buildMapValues = function (f) { if (_this.size === 0) { return _this.context.empty(); } if (undefined !== _this.source) return _this.source.mapValues(f); var entries = _this.entries.length === 0 ? null : base_1.Arr.mapSparse(_this.entries, function (e) { return [ e[0], f(e[1], e[0]), ]; }); var entrySets = _this.entrySets.length === 0 ? null : base_1.Arr.mapSparse(_this.entrySets, function (entrySet) { return entrySet.buildMapValues(f); }); return _this.context.block(entries, entrySets, _this.size, _this.level); }; return _this; } HashMapBlockBuilder.prototype.checkLock = function () { if (this._lock) base_1.RimbuError.throwModifiedBuilderWhileLoopingOverItError(); }; HashMapBlockBuilder.prototype.prepareMutate = function () { var _this = this; var _a; if (undefined === this._entries) { if (undefined !== this.source) { this._entries = null === this.source.entries ? [] : base_1.Arr.copySparse(this.source.entries); } else { this._entries = []; } } if (undefined === this._entrySets) { if (undefined !== this.source) { this._entrySets = null === this.source.entrySets ? [] : ((_a = base_1.Arr.mapSparse(this.source.entrySets, function (entrySet) { if (_this.context.isHashMapBlock(entrySet)) { return new HashMapBlockBuilder(_this.context, entrySet); } return new HashMapCollisionBuilder(_this.context, entrySet); })) !== null && _a !== void 0 ? _a : []); } else { this._entrySets = []; } } }; Object.defineProperty(HashMapBlockBuilder.prototype, "entries", { get: function () { this.prepareMutate(); return this._entries; }, enumerable: false, configurable: true }); Object.defineProperty(HashMapBlockBuilder.prototype, "entrySets", { get: function () { this.prepareMutate(); return this._entrySets; }, enumerable: false, configurable: true }); HashMapBlockBuilder.prototype.addEntryInternal = function (entry, hash) { if (hash === void 0) { hash = this.context.hash(entry[0]); } var keyIndex = this.context.getKeyIndex(this.level, hash); if (keyIndex in this.entries) { var currentEntry = this.entries[keyIndex]; if (this.context.eq(entry[0], currentEntry[0])) { if (Object.is(entry[1], currentEntry[1])) return false; this.source = undefined; this.entries[keyIndex] = entry; return true; } this.source = undefined; this.size++; delete this.entries[keyIndex]; if (this.level < this.context.maxDepth) { var newEntrySet_1 = new HashMapBlockBuilder(this.context, undefined, undefined, undefined, 0, this.level + 1); newEntrySet_1.addEntryInternal(currentEntry); newEntrySet_1.addEntryInternal(entry, hash); this.entrySets[keyIndex] = newEntrySet_1; return true; } var newEntries = list_1.List.builder(); newEntries.append(currentEntry); newEntries.append(entry); var newEntrySet = new HashMapCollisionBuilder(this.context, undefined, newEntries); this.entrySets[keyIndex] = newEntrySet; return true; } if (keyIndex in this.entrySets) { var currentEntrySet = this.entrySets[keyIndex]; var preSize = currentEntrySet.size; var changed = currentEntrySet.addEntryInternal(entry, hash); if (changed) this.source = undefined; this.size += currentEntrySet.size - preSize; return changed; } this.source = undefined; this.size++; this.entries[keyIndex] = entry; return true; }; HashMapBlockBuilder.prototype.buildNE = function () { if (undefined !== this.source) return this.source; var entries = this.entries.length === 0 ? null : base_1.Arr.copySparse(this.entries); var entrySets = this.entrySets.length === 0 ? null : base_1.Arr.mapSparse(this.entrySets, function (entrySet) { return entrySet.buildNE(); }); return this.context.block(entries, entrySets, this.size, this.level); }; return HashMapBlockBuilder; }(common_2.BlockBuilderBase)); exports.HashMapBlockBuilder = HashMapBlockBuilder; var HashMapCollisionBuilder = /** @class */ (function (_super) { tslib_1.__extends(HashMapCollisionBuilder, _super); function HashMapCollisionBuilder(context, source, _entries) { var _this = _super.call(this) || this; _this.context = context; _this.source = source; _this._entries = _entries; return _this; } HashMapCollisionBuilder.prototype.get = function (key, otherwise, hash) { var _this = this; if (!this.context.hasher.isValid(key)) return (0, common_1.OptLazy)(otherwise); if (undefined !== this.source) return this.source.get(key, otherwise); var token = Symbol(); var result = token; this.entries.forEach(function (e, _, halt) { if (_this.context.eq(key, e[0])) { result = e[1]; halt(); } }); if (token === result) return (0, common_1.OptLazy)(otherwise); return result; }; HashMapCollisionBuilder.prototype.addEntryInternal = function (entry) { var _this = this; var index = -1; this.entries.forEach(function (e, i, halt) { if (_this.context.eq(e[0], entry[0])) { index = i; halt(); } }); if (index < 0) { this.source = undefined; this.entries.append(entry); return true; } var oldEntry = this.entries.updateAt(index, function (currentEntry) { if (Object.is(currentEntry[1], entry[1])) return currentEntry; return entry; }); var changed = undefined === oldEntry || !Object.is(oldEntry[1], entry[1]) || !Object.is(oldEntry[0], entry[0]); if (changed) { this.source = undefined; } return changed; }; HashMapCollisionBuilder.prototype.set = function (key, value) { return this.addEntryInternal([key, value]); }; HashMapCollisionBuilder.prototype.modifyAt = function (atKey, options) { var _this = this; var index = -1; var foundEntry = undefined; this.entries.forEach(function (e, i, halt) { if (_this.context.eq(e[0], atKey)) { index = i; foundEntry = e; halt(); } }); if (undefined === foundEntry) { if (undefined === options.ifNew) return false; var newValue_3 = (0, common_1.OptLazyOr)(options.ifNew, base_1.Token); if (base_1.Token === newValue_3) return false; this.source = undefined; this.entries.append([atKey, newValue_3]); return true; } if (undefined === options.ifExists) return false; var newValue = options.ifExists instanceof Function ? options.ifExists(foundEntry[1], base_1.Token) : options.ifExists; if (Object.is(newValue, foundEntry[1])) return false; if (base_1.Token === newValue) { this.source = undefined; this.entries.remove(index); return true; } var result = this.entries.set(index, [atKey, newValue]); var changed = undefined !== result; if (changed) this.source = undefined; return changed; }; HashMapCollisionBuilder.prototype.buildNE = function () { if (undefined !== this.source) return this.source; return this.context.collision(this.entries.build().assumeNonEmpty()); }; HashMapCollisionBuilder.prototype.buildMapValues = function (f) { if (undefined !== this.source) return this.source.mapValues(f); return this.context.collision(this.entries .buildMap(function (entry) { return [ entry[0], f(entry[1], entry[0]), ]; }) .assumeNonEmpty()); }; return HashMapCollisionBuilder; }(common_2.CollisionBuilderBase)); exports.HashMapCollisionBuilder = HashMapCollisionBuilder; //# sourceMappingURL=builder.cjs.map