EdgeAIG's picture
download
raw
24.2 kB
/**
* @since 2.0.0
*/
import * as Equal_ from "../Equal.js";
import { format } from "../Formatter.js";
import { dual, pipe } from "../Function.js";
import * as Hash from "../Hash.js";
import { NodeInspectSymbol, toJson } from "../Inspectable.js";
import * as Option from "../Option.js";
import { pipeArguments } from "../Pipeable.js";
import { hasProperty } from "../Predicate.js";
import * as Result from "../Result.js";
/** @internal */
export const HashMapTypeId = "~effect/collections/HashMap";
// HAMT Implementation
/** @internal */
const SHIFT = 5;
/** @internal */
const BUCKET_SIZE = 1 << SHIFT; // 32
/** @internal */
// const BITMAP_SIZE = 1 << SHIFT // 32
/** @internal */
const MIN_ARRAY_NODE = BUCKET_SIZE / 4; // 8
/** @internal */
const MAX_INDEX_NODE = BUCKET_SIZE / 2; // 16
/** @internal */
const BITMAP_INDEX_MASK = BUCKET_SIZE - 1; // 31
/** @internal */
const popcount = n => {
n = n - (n >>> 1 & 0x55555555);
n = (n & 0x33333333) + (n >>> 2 & 0x33333333);
return (n + (n >>> 4) & 0xF0F0F0F) * 0x1010101 >>> 24;
};
/** @internal */
const mask = (hash, shift) => hash >>> shift & BITMAP_INDEX_MASK;
/** @internal */
const bitpos = (hash, shift) => 1 << mask(hash, shift);
/** @internal */
const index = (bitmap, bit) => popcount(bitmap & bit - 1);
/** @internal */
function mergeLeaves(edit, shift, hash1, node1, hash2, node2) {
if (shift > 32) {
throw new Error("HashMap: max depth exceeded");
}
const bit1 = bitpos(hash1, shift);
const bit2 = bitpos(hash2, shift);
if (bit1 === bit2) {
const child = mergeLeaves(edit, shift + SHIFT, hash1, node1, hash2, node2);
return new IndexedNode(edit, bit1, [child]);
}
const bitmap = bit1 | bit2;
const children = bit1 >>> 0 < bit2 >>> 0 ? [node1, node2] : [node2, node1];
return new IndexedNode(edit, bitmap, children);
}
/** @internal */
class Node {
canEdit(edit) {
return this.edit === edit;
}
}
/** @internal */
class EmptyNode extends Node {
_tag = "EmptyNode";
edit = 0;
get size() {
return 0;
}
get(_shift, _hash, _key) {
return Option.none();
}
has(_shift, _hash, _key) {
return false;
}
set(edit, _shift, hash, key, value, added) {
added.value = true;
return new LeafNode(edit, hash, key, value);
}
remove(_edit, _shift, _hash, _key, _removed) {
return this;
}
iterator() {
return [][Symbol.iterator]();
}
[Symbol.iterator]() {
return this.iterator();
}
canEdit(_edit) {
return false;
}
}
/** @internal */
class LeafNode extends Node {
_tag = "LeafNode";
edit;
hash;
key;
value;
constructor(edit, hash, key, value) {
super();
this.edit = edit;
this.hash = hash;
this.key = key;
this.value = value;
}
get size() {
return 1;
}
get(_shift, hash, key) {
if (this.hash === hash && Equal_.equals(this.key, key)) {
return Option.some(this.value);
}
return Option.none();
}
has(_shift, hash, key) {
return this.hash === hash && Equal_.equals(this.key, key);
}
set(edit, shift, hash, key, value, added) {
if (this.hash === hash && Equal_.equals(this.key, key)) {
if (Equal_.equals(this.value, value)) {
return this;
}
// Can mutate in-place if edit matches
if (this.canEdit(edit)) {
this.value = value;
return this;
}
return new LeafNode(edit, hash, key, value);
}
added.value = true;
if (this.hash === hash) {
return new CollisionNode(edit, hash, [[this.key, this.value], [key, value]]);
}
const newBit = bitpos(hash, shift);
const existingBit = bitpos(this.hash, shift);
if (newBit === existingBit) {
return new IndexedNode(edit, newBit, [this.set(edit, shift + SHIFT, hash, key, value, added)]);
}
const bitmap = newBit | existingBit;
const nodes = newBit >>> 0 < existingBit >>> 0 ? [new LeafNode(edit, hash, key, value), this] : [this, new LeafNode(edit, hash, key, value)];
return new IndexedNode(edit, bitmap, nodes);
}
remove(_edit, _shift, hash, key, removed) {
if (this.hash === hash && Equal_.equals(this.key, key)) {
removed.value = true;
return undefined;
}
return this;
}
iterator() {
return [[this.key, this.value]][Symbol.iterator]();
}
[Symbol.iterator]() {
return this.iterator();
}
}
/** @internal */
class CollisionNode extends Node {
_tag = "CollisionNode";
edit;
hash;
entries;
constructor(edit, hash, entries) {
super();
this.edit = edit;
this.hash = hash;
this.entries = entries;
}
get size() {
return this.entries.length;
}
get(_shift, hash, key) {
if (this.hash !== hash) {
return Option.none();
}
for (const [k, v] of this.entries) {
if (Equal_.equals(k, key)) {
return Option.some(v);
}
}
return Option.none();
}
has(_shift, hash, key) {
if (this.hash !== hash) {
return false;
}
for (const [k] of this.entries) {
if (Equal_.equals(k, key)) {
return true;
}
}
return false;
}
set(edit, shift, hash, key, value, added) {
if (this.hash !== hash) {
added.value = true;
// Need to merge this collision node with new leaf
return mergeLeaves(edit, shift, this.hash, this, hash, new LeafNode(edit, hash, key, value));
}
// Same hash - update or add to collision list
for (let i = 0; i < this.entries.length; i++) {
if (Equal_.equals(this.entries[i][0], key)) {
if (Equal_.equals(this.entries[i][1], value)) {
return this;
}
if (this.canEdit(edit)) {
this.entries[i] = [key, value];
return this;
}
const newEntries = [...this.entries];
newEntries[i] = [key, value];
return new CollisionNode(edit, this.hash, newEntries);
}
}
added.value = true;
if (this.canEdit(edit)) {
this.entries.push([key, value]);
return this;
}
return new CollisionNode(edit, this.hash, [...this.entries, [key, value]]);
}
remove(edit, _shift, hash, key, removed) {
if (this.hash !== hash) {
return this;
}
const idx = this.entries.findIndex(([k]) => Equal_.equals(k, key));
if (idx === -1) {
return this;
}
removed.value = true;
if (this.entries.length === 1) {
return undefined;
}
if (this.entries.length === 2) {
const remaining = this.entries[idx === 0 ? 1 : 0];
return new LeafNode(edit, this.hash, remaining[0], remaining[1]);
}
if (this.canEdit(edit)) {
this.entries.splice(idx, 1);
return this;
}
const newEntries = [...this.entries];
newEntries.splice(idx, 1);
return new CollisionNode(edit, this.hash, newEntries);
}
iterator() {
return this.entries[Symbol.iterator]();
}
[Symbol.iterator]() {
return this.iterator();
}
}
/** @internal */
class IndexedNode extends Node {
_tag = "IndexedNode";
edit;
_size;
bitmap;
children;
constructor(edit, bitmap, children) {
super();
this.edit = edit;
this.bitmap = bitmap;
this.children = children;
}
get size() {
if (this._size === undefined) {
this._size = this.children.reduce((acc, child) => acc + child.size, 0);
}
return this._size;
}
get(shift, hash, key) {
const bit = bitpos(hash, shift);
if ((this.bitmap & bit) === 0) {
return Option.none();
}
const idx = index(this.bitmap, bit);
return this.children[idx].get(shift + SHIFT, hash, key);
}
has(shift, hash, key) {
const bit = bitpos(hash, shift);
if ((this.bitmap & bit) === 0) {
return false;
}
const idx = index(this.bitmap, bit);
return this.children[idx].has(shift + SHIFT, hash, key);
}
set(edit, shift, hash, key, value, added) {
const bit = bitpos(hash, shift);
const idx = index(this.bitmap, bit);
if ((this.bitmap & bit) !== 0) {
// Existing child - update it
const child = this.children[idx];
const newChild = child.set(edit, shift + SHIFT, hash, key, value, added);
if (child === newChild) {
return this;
}
if (this.canEdit(edit)) {
this.children[idx] = newChild;
return this;
}
const newChildren = [...this.children];
newChildren[idx] = newChild;
return new IndexedNode(edit, this.bitmap, newChildren);
} else {
// New child - insert
added.value = true;
const newChild = new LeafNode(edit, hash, key, value);
const newBitmap = this.bitmap | bit;
if (this.canEdit(edit)) {
this.children.splice(idx, 0, newChild);
this.bitmap = newBitmap;
this._size = undefined;
if (this.children.length > MAX_INDEX_NODE) {
return this.expand(edit, newBitmap, this.children);
}
return this;
}
const newChildren = [...this.children];
newChildren.splice(idx, 0, newChild);
if (newChildren.length > MAX_INDEX_NODE) {
return this.expand(edit, newBitmap, newChildren);
}
return new IndexedNode(edit, newBitmap, newChildren);
}
}
remove(edit, shift, hash, key, removed) {
const bit = bitpos(hash, shift);
if ((this.bitmap & bit) === 0) {
return this;
}
const idx = index(this.bitmap, bit);
const child = this.children[idx];
const newChild = child.remove(edit, shift + SHIFT, hash, key, removed);
if (!removed.value) {
return this;
}
if (newChild === undefined) {
const newBitmap = this.bitmap ^ bit;
if (newBitmap === 0) {
return undefined;
}
if (this.children.length === 2) {
const remaining = this.children[idx === 0 ? 1 : 0];
if (remaining._tag === "LeafNode") {
return remaining;
}
}
if (this.canEdit(edit)) {
this.children.splice(idx, 1);
this.bitmap = newBitmap;
this._size = undefined;
return this;
}
const newChildren = [...this.children];
newChildren.splice(idx, 1);
return new IndexedNode(edit, newBitmap, newChildren);
}
if (child === newChild) {
return this;
}
if (this.canEdit(edit)) {
this.children[idx] = newChild;
return this;
}
const newChildren = [...this.children];
newChildren[idx] = newChild;
return new IndexedNode(edit, this.bitmap, newChildren);
}
expand(edit, bitmap, children) {
const nodes = new globalThis.Array(BUCKET_SIZE);
let j = 0;
for (let i = 0; i < BUCKET_SIZE; i++) {
if ((bitmap & 1 << i) !== 0) {
nodes[i] = children[j++];
}
}
return new ArrayNode(edit, children.length, nodes);
}
iterator() {
let childIndex = 0;
let currentIterator;
return {
next: () => {
while (childIndex < this.children.length) {
if (!currentIterator) {
currentIterator = this.children[childIndex].iterator();
}
const result = currentIterator.next();
if (!result.done) {
return result;
}
currentIterator = undefined;
childIndex++;
}
return {
done: true,
value: undefined
};
}
};
}
[Symbol.iterator]() {
return this.iterator();
}
}
/** @internal */
class ArrayNode extends Node {
_tag = "ArrayNode";
edit;
_size;
count;
children;
constructor(edit, count, children) {
super();
this.edit = edit;
this.count = count;
this.children = children;
}
get size() {
if (this._size === undefined) {
this._size = this.children.reduce((acc, child) => acc + (child?.size ?? 0), 0);
}
return this._size;
}
get(shift, hash, key) {
const idx = mask(hash, shift);
const child = this.children[idx];
return child ? child.get(shift + SHIFT, hash, key) : Option.none();
}
has(shift, hash, key) {
const idx = mask(hash, shift);
const child = this.children[idx];
return child ? child.has(shift + SHIFT, hash, key) : false;
}
set(edit, shift, hash, key, value, added) {
const idx = mask(hash, shift);
const child = this.children[idx];
if (child) {
const newChild = child.set(edit, shift + SHIFT, hash, key, value, added);
if (child === newChild) {
return this;
}
if (this.canEdit(edit)) {
this.children[idx] = newChild;
return this;
}
const newChildren = [...this.children];
newChildren[idx] = newChild;
return new ArrayNode(edit, this.count, newChildren);
} else {
added.value = true;
const newChild = new LeafNode(edit, hash, key, value);
if (this.canEdit(edit)) {
this.children[idx] = newChild;
this.count++;
this._size = undefined;
return this;
}
const newChildren = [...this.children];
newChildren[idx] = newChild;
return new ArrayNode(edit, this.count + 1, newChildren);
}
}
remove(edit, shift, hash, key, removed) {
const idx = mask(hash, shift);
const child = this.children[idx];
if (!child) {
return this;
}
const newChild = child.remove(edit, shift + SHIFT, hash, key, removed);
if (!removed.value) {
return this;
}
const newCount = this.count - (newChild ? 0 : 1);
if (newCount < MIN_ARRAY_NODE) {
return this.pack(edit, idx, newChild);
}
if (child === newChild) {
return this;
}
if (this.canEdit(edit)) {
this.children[idx] = newChild;
if (!newChild) {
this.count = newCount;
}
this._size = undefined;
return this;
}
const newChildren = [...this.children];
newChildren[idx] = newChild;
return new ArrayNode(edit, newCount, newChildren);
}
pack(edit, excludeIdx, newChild) {
const children = [];
let bitmap = 0;
let bit = 1;
for (let i = 0; i < this.children.length; i++) {
const child = i === excludeIdx ? newChild : this.children[i];
if (child) {
children.push(child);
bitmap |= bit;
}
bit <<= 1;
}
return new IndexedNode(edit, bitmap, children);
}
iterator() {
let childIndex = 0;
let currentIterator;
return {
next: () => {
while (childIndex < this.children.length) {
const child = this.children[childIndex];
if (!child) {
childIndex++;
continue;
}
if (!currentIterator) {
currentIterator = child.iterator();
}
const result = currentIterator.next();
if (!result.done) {
return result;
}
currentIterator = undefined;
childIndex++;
}
return {
done: true,
value: undefined
};
}
};
}
[Symbol.iterator]() {
return this.iterator();
}
}
/** @internal */
class HashMapImpl {
[HashMapTypeId] = HashMapTypeId;
_editable;
_edit;
_root;
_size;
constructor(editable, edit, root, size) {
this._editable = editable;
this._edit = edit;
this._root = root;
this._size = size;
}
get size() {
return this._size;
}
[Symbol.iterator]() {
return this._root.iterator();
}
[Equal_.symbol](that) {
if (isHashMap(that)) {
const thatImpl = that;
if (this.size !== thatImpl.size) {
return false;
}
for (const [key, value] of this) {
const otherValue = pipe(that, get(key));
if (Option.isNone(otherValue) || !Equal_.equals(value, otherValue.value)) {
return false;
}
}
return true;
}
return false;
}
[Hash.symbol]() {
let hash = Hash.string("HashMap");
for (const [key, value] of this) {
hash = hash ^ Hash.hash(key) + Hash.hash(value);
}
return hash;
}
[NodeInspectSymbol]() {
return toJson(this);
}
toString() {
return `HashMap(${format(Array.from(this))})`;
}
toJSON() {
return {
_id: "HashMap",
values: Array.from(this).map(([k, v]) => [toJson(k), toJson(v)])
};
}
pipe() {
return pipeArguments(this, arguments);
}
}
/** @internal */
const emptyNode = /*#__PURE__*/new EmptyNode();
/** @internal */
export const isHashMap = u => hasProperty(u, HashMapTypeId);
/** @internal */
export const empty = () => new HashMapImpl(false, 0, emptyNode, 0);
/** @internal */
export const make = (...entries) => fromIterable(entries);
/** @internal */
export const fromIterable = entries => {
let root = emptyNode;
let size = 0;
const added = {
value: false
};
for (const [key, value] of entries) {
const hash = Hash.hash(key);
added.value = false;
root = root.set(NaN, 0, hash, key, value, added);
if (added.value) {
size++;
}
}
return new HashMapImpl(false, 0, root, size);
};
/** @internal */
export const isEmpty = self => self.size === 0;
/** @internal */
export const get = /*#__PURE__*/dual(2, (self, key) => {
const impl = self;
return impl._root.get(0, Hash.hash(key), key);
});
/** @internal */
export const getHash = /*#__PURE__*/dual(3, (self, key, hash) => {
const impl = self;
return impl._root.get(0, hash, key);
});
/** @internal */
export const getUnsafe = /*#__PURE__*/dual(2, (self, key) => {
const result = get(self, key);
if (Option.isSome(result)) {
return result.value;
}
throw new Error(`HashMap.getUnsafe: key not found: ${key}`);
});
/** @internal */
export const has = /*#__PURE__*/dual(2, (self, key) => {
const impl = self;
return impl._root.has(0, Hash.hash(key), key);
});
/** @internal */
export const hasHash = /*#__PURE__*/dual(3, (self, key, hash) => {
const impl = self;
return impl._root.has(0, hash, key);
});
/** @internal */
export const hasBy = /*#__PURE__*/dual(2, (self, predicate) => {
for (const [key, value] of self) {
if (predicate(value, key)) {
return true;
}
}
return false;
});
/** @internal */
export const set = /*#__PURE__*/dual(3, (self, key, value) => {
const impl = self;
const hash = Hash.hash(key);
const added = {
value: false
};
// Pass edit context: use current edit if editable, otherwise NaN (never matches any edit)
const edit = impl._editable ? impl._edit : NaN;
const newRoot = impl._root.set(edit, 0, hash, key, value, added);
if (impl._editable) {
// In-place mutation
impl._root = newRoot;
if (added.value) {
impl._size++;
}
return self;
}
// Immutable: create new instance if changed
if (impl._root === newRoot) {
return self;
}
return new HashMapImpl(false, impl._edit, newRoot, impl._size + (added.value ? 1 : 0));
});
/** @internal */
export const keys = self => {
const iterator = self[Symbol.iterator]();
return {
[Symbol.iterator]() {
return this;
},
next() {
const result = iterator.next();
if (result.done) {
return {
done: true,
value: undefined
};
}
return {
done: false,
value: result.value[0]
};
}
};
};
/** @internal */
export const values = self => {
const iterator = self[Symbol.iterator]();
return {
[Symbol.iterator]() {
return this;
},
next() {
const result = iterator.next();
if (result.done) {
return {
done: true,
value: undefined
};
}
return {
done: false,
value: result.value[1]
};
}
};
};
/** @internal */
export const entries = self => {
const iterator = self[Symbol.iterator]();
return {
[Symbol.iterator]() {
return this;
},
next() {
return iterator.next();
}
};
};
/** @internal */
export const size = self => self.size;
/** @internal */
export const beginMutation = self => {
const impl = self;
return new HashMapImpl(true, impl._edit + 1, impl._root, impl._size);
};
/** @internal */
export const endMutation = self => {
const impl = self;
impl._editable = false;
return self;
};
/** @internal */
export const mutate = /*#__PURE__*/dual(2, (self, f) => {
const mutable = beginMutation(self);
f(mutable);
return endMutation(mutable);
});
/** @internal */
export const modifyAt = /*#__PURE__*/dual(3, (self, key, f) => {
const current = get(self, key);
const updated = f(current);
if (Option.isNone(updated)) {
return has(self, key) ? remove(self, key) : self;
}
return set(self, key, updated.value);
});
/** @internal */
export const modifyHash = /*#__PURE__*/dual(4, (self, key, hash, f) => {
const current = getHash(self, key, hash);
const updated = f(current);
if (Option.isNone(updated)) {
return hasHash(self, key, hash) ? remove(self, key) : self;
}
return set(self, key, updated.value);
});
/** @internal */
export const modify = /*#__PURE__*/dual(3, (self, key, f) => {
return modifyAt(self, key, Option.map(f));
});
/** @internal */
export const union = /*#__PURE__*/dual(2, (self, that) => {
let result = self;
for (const [key, value] of that) {
result = set(result, key, value);
}
return result;
});
/** @internal */
export const remove = /*#__PURE__*/dual(2, (self, key) => {
const impl = self;
const hash = Hash.hash(key);
const removed = {
value: false
};
const edit = impl._editable ? impl._edit : NaN;
const newRoot = impl._root.remove(edit, 0, hash, key, removed);
if (!removed.value) {
return self;
}
if (impl._editable) {
impl._root = newRoot ?? emptyNode;
impl._size--;
return self;
}
if (newRoot === undefined) {
return empty();
}
return new HashMapImpl(false, impl._edit, newRoot, impl._size - 1);
});
/** @internal */
export const removeMany = /*#__PURE__*/dual(2, (self, keys) => {
let result = self;
for (const key of keys) {
result = remove(result, key);
}
return result;
});
/** @internal */
export const setMany = /*#__PURE__*/dual(2, (self, entries) => {
let result = self;
for (const [key, value] of entries) {
result = set(result, key, value);
}
return result;
});
/** @internal */
export const map = /*#__PURE__*/dual(2, (self, f) => {
let result = empty();
for (const [key, value] of self) {
result = set(result, key, f(value, key));
}
return result;
});
/** @internal */
export const flatMap = /*#__PURE__*/dual(2, (self, f) => {
let result = empty();
for (const [key, value] of self) {
result = union(result, f(value, key));
}
return result;
});
/** @internal */
export const forEach = /*#__PURE__*/dual(2, (self, f) => {
for (const [key, value] of self) {
f(value, key);
}
});
/** @internal */
export const reduce = /*#__PURE__*/dual(3, (self, zero, f) => {
let result = zero;
for (const [key, value] of self) {
result = f(result, value, key);
}
return result;
});
/** @internal */
export const filter = /*#__PURE__*/dual(2, (self, f) => {
let result = empty();
for (const [key, value] of self) {
if (f(value, key)) {
result = set(result, key, value);
}
}
return result;
});
/** @internal */
export const compact = self => {
let result = empty();
for (const [key, value] of self) {
if (Option.isSome(value)) {
result = set(result, key, value.value);
}
}
return result;
};
/** @internal */
export const filterMap = /*#__PURE__*/dual(2, (self, f) => {
let result = empty();
for (const [key, value] of self) {
const mapped = f(value, key);
if (Result.isSuccess(mapped)) {
result = set(result, key, mapped.success);
}
}
return result;
});
/** @internal */
export const findFirst = /*#__PURE__*/dual(2, (self, predicate) => {
for (const [key, value] of self) {
if (predicate(value, key)) {
return Option.some([key, value]);
}
}
return Option.none();
});
/** @internal */
export const some = /*#__PURE__*/dual(2, (self, predicate) => {
for (const [key, value] of self) {
if (predicate(value, key)) {
return true;
}
}
return false;
});
/** @internal */
export const every = /*#__PURE__*/dual(2, (self, predicate) => {
for (const [key, value] of self) {
if (!predicate(value, key)) {
return false;
}
}
return true;
});
//# sourceMappingURL=hashMap.js.map

Xet Storage Details

Size:
24.2 kB
·
Xet hash:
6454eca46d15c14e560e926f74349056814a8d33e0d7512d8188f9b51acc225c

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.