diff --git a/pkg/interface/src/logic/state/base.ts b/pkg/interface/src/logic/state/base.ts index b12b9f203d..29fea66ce6 100644 --- a/pkg/interface/src/logic/state/base.ts +++ b/pkg/interface/src/logic/state/base.ts @@ -1,8 +1,10 @@ -import produce from "immer"; +import produce, { setAutoFreeze } from "immer"; import { compose } from "lodash/fp"; import create, { State, UseStore } from "zustand"; import { persist, devtools } from "zustand/middleware"; +setAutoFreeze(false); + export const stateSetter = ( fn: (state: StateType) => void, diff --git a/pkg/npm/api/lib/BigIntOrderedMap.ts b/pkg/npm/api/lib/BigIntOrderedMap.ts index d7cd00bdf1..ce6e0a253f 100644 --- a/pkg/npm/api/lib/BigIntOrderedMap.ts +++ b/pkg/npm/api/lib/BigIntOrderedMap.ts @@ -1,227 +1,61 @@ -import { BigInteger } from "big-integer"; -import { immerable } from 'immer'; +import _ from 'lodash'; +import bigInt, { BigInteger } from "big-integer"; -interface NonemptyNode { - n: [BigInteger, V]; - l: MapNode; - r: MapNode; +function sortBigInt(a: BigInteger, b: BigInteger) { + if (a.lt(b)) { + return 1; + } else if (a.eq(b)) { + return 0; + } else { + return -1; + } } - -type MapNode = NonemptyNode | null; - -/** - * An implementation of ordered maps for JS - * Plagiarised wholesale from sys/zuse - */ export default class BigIntOrderedMap implements Iterable<[BigInteger, V]> { - private root: MapNode = null; - [immerable] = true; - size: number = 0; + private root: Record = {} + private cachedIter: [BigInteger, V][] | null = null; - constructor(initial: [BigInteger, V][] = []) { - initial.forEach(([key, val]) => { + constructor(items: [BigInteger, V][] = []) { + _.forEach(items, ([key, val]) => { this.set(key, val); }); + this.generateCachedIter(); } - /** - * Retrieve an value for a key - */ - get(key: BigInteger): V | null { - const inner = (node: MapNode): V | null => { - if (!node) { - return null; - } - const [k, v] = node.n; - if (key.eq(k)) { - return v; - } - if (key.gt(k)) { - return inner(node.l); - } else { - return inner(node.r); - } - }; - - return inner(this.root); + get size() { + return this.cachedIter?.length ?? Object.keys(this.root).length; } - /** - * Put an item by a key - */ - set(key: BigInteger, value: V): void { - const inner = (node: MapNode): MapNode => { - if (!node) { - return { - n: [key, value], - l: null, - r: null, - }; - } - const [k] = node.n; - if (key.eq(k)) { - this.size--; - return { - ...node, - n: [k, value], - }; - } - if (key.gt(k)) { - const l = inner(node.l); - if (!l) { - throw new Error("invariant violation"); - } - return { - ...node, - l, - }; - } - const r = inner(node.r); - if (!r) { - throw new Error("invariant violation"); - } - - return { ...node, r }; - }; - this.size++; - this.root = inner(this.root); + get(key: BigInteger) { + return this.root[key.toString()] ?? null; + } + + set(key: BigInteger, value: V) { + this.root[key.toString()] = value; + this.cachedIter = null; } - /** - * Remove all entries - */ clear() { - this.root = null; + this.cachedIter = null; + this.root = {} } - /** - * Predicate testing if map contains key - */ - has(key: BigInteger): boolean { - const inner = (node: MapNode): boolean => { - if (!node) { - return false; - } - const [k] = node.n; - - if (k.eq(key)) { - return true; - } - if (key.gt(k)) { - return inner(node.l); - } - return inner(node.r); - }; - return inner(this.root); + has(key: BigInteger) { + return key.toString() in this.root; } - /** - * Remove value associated with key, returning whether that key - * existed in the first place - */ delete(key: BigInteger) { - const inner = (node: MapNode): [boolean, MapNode] => { - if (!node) { - return [false, null]; - } - const [k] = node.n; - if (k.eq(key)) { - return [true, this.nip(node)]; - } - if (key.gt(k)) { - const [bool, l] = inner(node.l); - return [ - bool, - { - ...node, - l, - }, - ]; - } - - const [bool, r] = inner(node.r); - return [ - bool, - { - ...node, - r, - }, - ]; - }; - const [ret, newRoot] = inner(this.root); - if(ret) { - this.size--; + const had = this.has(key); + if(had) { + delete this.root[key.toString()]; + this.cachedIter = null; } - this.root = newRoot; - return ret; - } - - private nip(nod: NonemptyNode): MapNode { - const inner = (node: NonemptyNode): MapNode => { - if (!node.l) { - return node.r; - } - if (!node.r) { - return node.l; - } - return { - ...node.l, - r: inner(node.r), - }; - }; - return inner(nod); - } - - peekLargest(): [BigInteger, V] | undefined { - const inner = (node: MapNode): [BigInteger, V] | undefined => { - if(!node) { - return undefined; - } - if(node.l) { - return inner(node.l); - } - return node.n; - } - return inner(this.root); - } - - peekSmallest(): [BigInteger, V] | undefined { - const inner = (node: MapNode): [BigInteger, V] | undefined => { - if(!node) { - return undefined; - } - if(node.r) { - return inner(node.r); - } - return node.n; - } - return inner(this.root); - } - - keys(): BigInteger[] { - const list = Array.from(this); - return list.map(([key]) => key); - } - - forEach(f: (value: V, key: BigInteger) => void) { - const list = Array.from(this); - return list.forEach(([k,v]) => f(v,k)); + return had; } [Symbol.iterator](): IterableIterator<[BigInteger, V]> { - let result: [BigInteger, V][] = []; - const inner = (node: MapNode) => { - if (!node) { - return; - } - inner(node.l); - result.push(node.n); - inner(node.r); - }; - inner(this.root); - let idx = 0; + const result = this.generateCachedIter(); return { [Symbol.iterator]: this[Symbol.iterator], next: (): IteratorResult<[BigInteger, V]> => { @@ -232,4 +66,31 @@ export default class BigIntOrderedMap implements Iterable<[BigInteger, V]> { }, }; } + + peekLargest() { + const sorted = Array.from(this); + return sorted[0] as [BigInteger, V] | null; + } + + peekSmallest() { + const sorted = Array.from(this); + return sorted[sorted.length - 1] as [BigInteger, V] | null; + } + + keys() { + return Object.keys(this.root).map(k => bigInt(k)).sort(sortBigInt) + } + + private generateCachedIter() { + if(this.cachedIter) { + return this.cachedIter; + } + const result = Object.keys(this.root).map(key => { + const num = bigInt(key); + return [num, this.root[key]] as [BigInteger, V]; + }).sort(([a], [b]) => sortBigInt(a,b)); + this.cachedIter = result; + return result; + } } +