diff --git a/src/vanilla/utils.ts b/src/vanilla/utils.ts index e694eb64..0e45446c 100644 --- a/src/vanilla/utils.ts +++ b/src/vanilla/utils.ts @@ -3,8 +3,4 @@ export { watch } from './utils/watch.ts' export { devtools } from './utils/devtools.ts' export { deepClone } from './utils/deepClone.ts' export { proxySet } from './utils/proxySet.ts' -export { proxyMap } from './utils/proxyMap-indexMap-keyval.ts' - -export { proxyMap as newProxyMapKeyVals } from './utils/proxyMap-indexMap-keyval.ts' -export { proxyMap as newProxyMapKeyVals2 } from './utils/proxyMap-indexMap-key-val.ts' -export { proxyMap as proxyMapRawMap } from './utils/proxyMap-rawMap1.ts' +export { proxyMap } from './utils/proxyMap.ts' diff --git a/src/vanilla/utils/proxyMap-chunked.ts b/src/vanilla/utils/proxyMap-chunked.ts deleted file mode 100644 index c92ae99b..00000000 --- a/src/vanilla/utils/proxyMap-chunked.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { proxy, unstable_getInternalStates } from '../../vanilla.ts' -const { proxyStateMap } = unstable_getInternalStates() -const maybeProxify = (x: any) => (typeof x === 'object' ? proxy({ x }).x : x) -const isProxy = (x: any) => proxyStateMap.has(x) -const CHUNK_SIZE = 1000 - -type InternalProxyObject = Map & { - data: Array> - size: number - toJSON: () => Map -} - -export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { - const data: Array> = [] - const indexMap = new Map() - const emptyIndexes: { chunkIndex: number; position: number }[] = [] - - if (entries !== null && typeof entries !== 'undefined') { - if (typeof entries[Symbol.iterator] !== 'function') { - throw new TypeError( - 'proxyMap:\n\tinitial state must be iterable\n\t\ttip: structure should be [[key, value]]', - ) - } - for (const [k, v] of entries) { - const key = maybeProxify(k) - const value = maybeProxify(v) - let currentChunkIndex = 0 - if (data.length === 0) { - // create new chunk if none exist - data.push([]) - } else { - const lastChunk = data[data.length - 1]! - if (lastChunk.length === CHUNK_SIZE) { - // create new chunk if the last chunk is full - data.push([]) - currentChunkIndex = data.length - 1 - } - } - - indexMap.set(key, { - chunkIndex: currentChunkIndex, - position: data[currentChunkIndex]!.length, - }) - data[currentChunkIndex]!.push([key, value]) - } - } - - const vObject: InternalProxyObject = { - data, - get size() { - return indexMap.size - }, - get(key: K) { - const k = maybeProxify(key) - if (!indexMap.has(k) && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.data.length - } - if (indexMap.has(k)) { - const { chunkIndex, position } = indexMap.get(k)! - if (this.data[chunkIndex]![position] !== undefined) { - return this.data[chunkIndex]![position]![1] - } - } - return undefined - }, - has(key: K) { - const k = maybeProxify(key) - if (!indexMap.has(k) && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.data.length - } - return indexMap.has(k) - }, - set(key: K, value: V) { - if (!isProxy(this)) { - if (import.meta.env?.MODE !== 'production') { - throw new Error('Cannot perform mutations on a snapshot') - } else { - return this - } - } - const k = maybeProxify(key) - const v = maybeProxify(value) - const indices = indexMap.get(k) - - if (indices) { - // Key exists, update the value - const { chunkIndex, position } = indices - this.data[chunkIndex]![position]![1] = v - } else { - // Key does not exist, insert it - let chunkIndex: number - let position: number - - if (emptyIndexes.length > 0) { - // Reuse an empty position - const emptyIndex = emptyIndexes.pop()! - chunkIndex = emptyIndex.chunkIndex - position = emptyIndex.position - this.data[chunkIndex]![position] = [k, v] - } else { - // No empty positions, add to the last chunk or create a new one - if ( - this.data.length === 0 || - this.data[this.data.length - 1]!.length === CHUNK_SIZE - ) { - // Need to create a new chunk - this.data.push(proxy([])) - } - chunkIndex = this.data.length - 1 - position = this.data[chunkIndex]!.length - this.data[chunkIndex]!.push([k, v]) - } - // Update the index map with new indices - indexMap.set(k, { chunkIndex, position }) - } - return this - }, - delete(key: K) { - if (!isProxy(this)) { - if (import.meta.env?.MODE !== 'production') { - throw new Error('Cannot perform mutations on a snapshot') - } else { - return false - } - } - const k = maybeProxify(key) - if (indexMap.has(k)) { - const { chunkIndex, position } = indexMap.get(k)! - delete this.data[chunkIndex]![position] - emptyIndexes.push({ chunkIndex, position }) - indexMap.delete(k) - return true - } - return false - }, - clear() { - if (!isProxy(this)) { - if (import.meta.env?.MODE !== 'production') { - throw new Error('Cannot perform mutations on a snapshot') - } else { - return - } - } - indexMap.clear() - this.data.splice(0) - emptyIndexes.splice(0) - }, - forEach(cb: (value: V, key: K, map: Map) => void) { - indexMap.forEach(({ chunkIndex, position }) => { - const item = this.data[chunkIndex]![position]! - cb(item[1]!, item[0]!, this) - }) - }, - *entries(): IterableIterator<[K, V]> { - for (const { chunkIndex, position } of indexMap.values()) { - yield this.data[chunkIndex]![position] as [K, V] - } - }, - *keys(): IterableIterator { - for (const key of indexMap.keys()) { - yield key - } - }, - *values(): IterableIterator { - for (const { chunkIndex, position } of indexMap.values()) { - yield this.data[chunkIndex]![position]![1]! - } - }, - [Symbol.iterator]() { - return this.entries() - }, - get [Symbol.toStringTag]() { - return 'Map' - }, - toJSON(): Map { - return new Map( - [...indexMap].map(([k, { chunkIndex, position }]) => [ - k, - this.data[chunkIndex]![position]![1]!, - ]), - ) - }, - } - - const proxiedObject = proxy(vObject) - - Object.defineProperties(proxiedObject, { - size: { enumerable: false }, - data: { enumerable: false }, - toJSON: { enumerable: false }, - }) - - Object.seal(proxiedObject) - - return proxiedObject as unknown as Map & { - $$valtioSnapshot: Omit, 'set' | 'delete' | 'clear'> - } -} diff --git a/src/vanilla/utils/proxyMap-indexMap-filled.ts b/src/vanilla/utils/proxyMap-indexMap-filled.ts deleted file mode 100644 index e8d8fc1e..00000000 --- a/src/vanilla/utils/proxyMap-indexMap-filled.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { proxy, unstable_getInternalStates } from '../../vanilla.ts' -const { proxyStateMap } = unstable_getInternalStates() -const maybeProxify = (x: any) => (typeof x === 'object' ? proxy({ x }).x : x) -const isProxy = (x: any) => proxyStateMap.has(x) - -type InternalProxyObject = Map & { - data: Array<[K, V] | undefined> - nextIndex: number - toJSON: () => Map -} - -const MIN_DATA_SIZE = 1000 - -export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { - const initialData: Array<[K, V] | undefined> = new Array(MIN_DATA_SIZE).fill( - undefined, - ) - let initialNextIndex = 0 - const indexMap = new Map() - const emptyIndexes: number[] = [] - - if (entries !== null && typeof entries !== 'undefined') { - if (typeof entries[Symbol.iterator] !== 'function') { - throw new TypeError( - 'proxyMap:\n\tinitial state must be iterable\n\t\ttip: structure should be [[key, value]]', - ) - } - for (const [k, v] of entries) { - const key = maybeProxify(k) - const value = maybeProxify(v) - indexMap.set(key, initialNextIndex) - initialData[initialNextIndex++] = [key, value] - } - } - - const vObject: InternalProxyObject = { - data: initialData, - nextIndex: initialNextIndex, - get size() { - return indexMap.size - }, - get(key: K) { - const k = maybeProxify(key) - const index = indexMap.get(k) - if (index === undefined && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.nextIndex - } - if (index !== undefined) { - return this.data[index]![1] - } - return undefined - }, - has(key: K) { - const k = maybeProxify(key) - const exists = indexMap.has(k) - if (!exists && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.nextIndex - } - return exists - }, - set(key: K, value: V) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const v = maybeProxify(value) - const index = indexMap.get(k) - if (index === undefined) { - const i = emptyIndexes.length ? emptyIndexes.pop()! : this.nextIndex++ - indexMap.set(k, i) - this.data[i] = [k, v] - } else { - this.data[index]![1] = v - } - return this - }, - delete(key: K) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const index = indexMap.get(k) - if (index !== undefined) { - this.data[index] = undefined - indexMap.delete(k) - emptyIndexes.push(index) - return true - } - return false - }, - clear() { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - indexMap.clear() - this.data.splice(MIN_DATA_SIZE).fill(undefined) - this.nextIndex = 0 - emptyIndexes.splice(0) - }, - forEach(cb: (value: V, key: K, map: Map) => void) { - indexMap.forEach((index) => { - cb(this.data[index]![1]!, this.data[index]![0]!, this) - }) - }, - *entries(): IterableIterator<[K, V]> { - for (const index of indexMap.values()) { - yield this.data[index] as [K, V] - } - }, - *keys(): IterableIterator { - for (const key of indexMap.keys()) { - yield key - } - }, - *values(): IterableIterator { - for (const index of indexMap.values()) { - yield this.data[index]![1]! - } - }, - [Symbol.iterator]() { - return this.entries() - }, - get [Symbol.toStringTag]() { - return 'Map' - }, - toJSON(): Map { - return new Map([...indexMap].map(([k, v]) => [k, this.data[v]![1]!])) - }, - } - - const proxiedObject = proxy(vObject) - - Object.defineProperties(proxiedObject, { - size: { enumerable: false }, - data: { enumerable: false }, - nextIndex: { enumerable: false }, - toJSON: { enumerable: false }, - }) - - Object.seal(proxiedObject) - - return proxiedObject as unknown as Map & { - $$valtioSnapshot: Omit, 'set' | 'delete' | 'clear'> - } -} diff --git a/src/vanilla/utils/proxyMap-indexMap-key-val.ts b/src/vanilla/utils/proxyMap-indexMap-key-val.ts deleted file mode 100644 index 8c48a0ac..00000000 --- a/src/vanilla/utils/proxyMap-indexMap-key-val.ts +++ /dev/null @@ -1,146 +0,0 @@ -import { proxy, unstable_getInternalStates } from '../../vanilla.ts' -const { proxyStateMap } = unstable_getInternalStates() -const maybeProxify = (x: any) => (typeof x === 'object' ? proxy({ x }).x : x) -const isProxy = (x: any) => proxyStateMap.has(x) - -type InternalProxyObject = Map & { - dataKeys: Array - dataValues: Array - index: number - toJSON: () => Map -} - -export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { - const initialDataKeys: Array = [] - const initialDataValues: Array = [] - let initialIndex = 0 - const indexMap = new Map() - - if (entries !== null && typeof entries !== 'undefined') { - if (typeof entries[Symbol.iterator] !== 'function') { - throw new TypeError( - 'proxyMap:\n\tinitial state must be iterable\n\t\ttip: structure should be [[key, value]]', - ) - } - for (const [k, v] of entries) { - const key = maybeProxify(k) - const value = maybeProxify(v) - indexMap.set(key, initialIndex) - initialDataKeys[initialIndex] = key - initialDataValues[initialIndex] = value - initialIndex++ - } - } - - const vObject: InternalProxyObject = { - dataKeys: initialDataKeys, - dataValues: initialDataValues, - index: initialIndex, - get size() { - return indexMap.size - }, - get(key: K) { - const k = maybeProxify(key) - const index = indexMap.get(k) - if (index === undefined && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.index - } - if (index !== undefined) { - return this.dataValues[index] - } - return undefined - }, - has(key: K) { - const k = maybeProxify(key) - const exists = indexMap.has(k) - if (!exists && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.index - } - return exists - }, - set(key: K, value: V) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const v = maybeProxify(value) - const index = indexMap.get(k) - if (index === undefined) { - indexMap.set(k, this.index) - this.dataKeys[this.index] = k - this.dataValues[this.index] = v - this.index++ - } else { - this.dataValues[index] = v - } - return this - }, - delete(key: K) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const index = indexMap.get(k) - if (index !== undefined) { - delete this.dataKeys[index] - delete this.dataValues[index] - indexMap.delete(k) - return true - } - return false - }, - clear() { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - indexMap.clear() - this.dataKeys.splice(0) - this.dataValues.splice(0) - }, - forEach(cb: (value: V, key: K, map: Map) => void) { - indexMap.forEach((index) => { - cb(this.dataValues[index]!, this.dataKeys[index]!, this) - }) - }, - *entries(): MapIterator<[K, V]> { - for (const index of indexMap.values()) { - yield [this.dataKeys[index], this.dataValues[index]] as [K, V] - } - }, - *keys(): IterableIterator { - for (const key of indexMap.keys()) { - yield key - } - }, - *values(): IterableIterator { - for (const index of indexMap.values()) { - yield this.dataValues[index]! - } - }, - [Symbol.iterator]() { - return this.entries() - }, - get [Symbol.toStringTag]() { - return 'Map' - }, - toJSON(): Map { - return new Map([...indexMap].map(([k, v]) => [k, this.dataValues[v]!])) - }, - } - - const proxiedObject = proxy(vObject) - - Object.defineProperties(proxiedObject, { - size: { enumerable: false }, - data: { enumerable: false }, - toJSON: { enumerable: false }, - }) - - Object.seal(proxiedObject) - - return proxiedObject as unknown as Map & { - $$valtioSnapshot: Omit, 'set' | 'delete' | 'clear'> - } -} diff --git a/src/vanilla/utils/proxyMap-indexMap-push.ts b/src/vanilla/utils/proxyMap-indexMap-push.ts deleted file mode 100644 index c60275cd..00000000 --- a/src/vanilla/utils/proxyMap-indexMap-push.ts +++ /dev/null @@ -1,143 +0,0 @@ -import { proxy, unstable_getInternalStates } from '../../vanilla.ts' -const { proxyStateMap } = unstable_getInternalStates() -const maybeProxify = (x: any) => (typeof x === 'object' ? proxy({ x }).x : x) -const isProxy = (x: any) => proxyStateMap.has(x) - -type InternalProxyObject = Map & { - data: Array<[K, V] | undefined> - toJSON: () => Map -} - -export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { - const initialData: Array<[K, V]> = [] - const indexMap = new Map() - const emptyIndexes: number[] = [] - - if (entries !== null && typeof entries !== 'undefined') { - if (typeof entries[Symbol.iterator] !== 'function') { - throw new TypeError( - 'proxyMap:\n\tinitial state must be iterable\n\t\ttip: structure should be [[key, value]]', - ) - } - for (const [k, v] of entries) { - const key = maybeProxify(k) - const value = maybeProxify(v) - indexMap.set(key, initialData.length) - initialData.push([key, value]) - } - } - - const vObject: InternalProxyObject = { - data: initialData, - get size() { - return indexMap.size - }, - get(key: K) { - const k = maybeProxify(key) - const index = indexMap.get(k) - if (index === undefined && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.data.length - } - if (index !== undefined) { - return this.data[index]![1] - } - return undefined - }, - has(key: K) { - const k = maybeProxify(key) - const exists = indexMap.has(k) - if (!exists && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.data.length - } - return exists - }, - set(key: K, value: V) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const v = maybeProxify(value) - const index = indexMap.get(k) - if (index === undefined) { - if (emptyIndexes.length) { - const i = emptyIndexes.pop()! - indexMap.set(k, i) - this.data[i] = [k, v] - } else { - indexMap.set(k, this.data.length) - this.data.push([k, v]) - } - } else { - this.data[index]![1] = v - } - return this - }, - delete(key: K) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const index = indexMap.get(k) - if (index !== undefined) { - this.data[index] = undefined - indexMap.delete(k) - emptyIndexes.push(index) - return true - } - return false - }, - clear() { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - indexMap.clear() - this.data.splice(0) - emptyIndexes.splice(0) - }, - forEach(cb: (value: V, key: K, map: Map) => void) { - indexMap.forEach((index) => { - cb(this.data[index]![1]!, this.data[index]![0]!, this) - }) - }, - *entries(): MapIterator<[K, V]> { - for (const index of indexMap.values()) { - yield this.data[index] as [K, V] - } - }, - *keys(): IterableIterator { - for (const key of indexMap.keys()) { - yield key - } - }, - *values(): IterableIterator { - for (const index of indexMap.values()) { - yield this.data[index]![1]! - } - }, - [Symbol.iterator]() { - return this.entries() - }, - get [Symbol.toStringTag]() { - return 'Map' - }, - toJSON(): Map { - return new Map([...indexMap].map(([k, v]) => [k, this.data[v]![1]!])) - }, - } - - const proxiedObject = proxy(vObject) - - Object.defineProperties(proxiedObject, { - size: { enumerable: false }, - data: { enumerable: false }, - toJSON: { enumerable: false }, - }) - - Object.seal(proxiedObject) - - return proxiedObject as unknown as Map & { - $$valtioSnapshot: Omit, 'set' | 'delete' | 'clear'> - } -} diff --git a/src/vanilla/utils/proxyMap-indexMap.ts b/src/vanilla/utils/proxyMap-indexMap.ts deleted file mode 100644 index baab3d12..00000000 --- a/src/vanilla/utils/proxyMap-indexMap.ts +++ /dev/null @@ -1,147 +0,0 @@ -import { proxy, unstable_getInternalStates } from '../../vanilla.ts' -const { proxyStateMap } = unstable_getInternalStates() -const maybeProxify = (x: any) => (typeof x === 'object' ? proxy({ x }).x : x) -const isProxy = (x: any) => proxyStateMap.has(x) - -type InternalProxyObject = Map & { - data: Array<[K, V] | undefined> - nextIndex: number - toJSON: () => Map -} - -const MIN_DATA_SIZE = 0 - -export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { - const initialData: Array<[K, V] | undefined> = new Array(MIN_DATA_SIZE).fill( - undefined, - ) - let initialNextIndex = 0 - const indexMap = new Map() - const emptyIndexes: number[] = [] - - if (entries !== null && typeof entries !== 'undefined') { - if (typeof entries[Symbol.iterator] !== 'function') { - throw new TypeError( - 'proxyMap:\n\tinitial state must be iterable\n\t\ttip: structure should be [[key, value]]', - ) - } - for (const [k, v] of entries) { - const key = maybeProxify(k) - const value = maybeProxify(v) - indexMap.set(key, initialNextIndex) - initialData[initialNextIndex++] = [key, value] - } - } - - const vObject: InternalProxyObject = { - data: initialData, - nextIndex: initialNextIndex, - get size() { - return indexMap.size - }, - get(key: K) { - const k = maybeProxify(key) - const index = indexMap.get(k) - if (index === undefined && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.nextIndex - } - if (index !== undefined) { - return this.data[index]![1] - } - return undefined - }, - has(key: K) { - const k = maybeProxify(key) - const exists = indexMap.has(k) - if (!exists && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.nextIndex - } - return exists - }, - set(key: K, value: V) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const v = maybeProxify(value) - const index = indexMap.get(k) - if (index === undefined) { - const i = emptyIndexes.length ? emptyIndexes.pop()! : this.nextIndex++ - indexMap.set(k, i) - this.data[i] = [k, v] - } else { - this.data[index]![1] = v - } - return this - }, - delete(key: K) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const index = indexMap.get(k) - if (index !== undefined) { - this.data[index] = undefined - indexMap.delete(k) - emptyIndexes.push(index) - return true - } - return false - }, - clear() { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - indexMap.clear() - this.data.splice(MIN_DATA_SIZE).fill(undefined) - this.nextIndex = 0 - emptyIndexes.splice(0) - }, - forEach(cb: (value: V, key: K, map: Map) => void) { - indexMap.forEach((index) => { - cb(this.data[index]![1]!, this.data[index]![0]!, this) - }) - }, - *entries(): IterableIterator<[K, V]> { - for (const index of indexMap.values()) { - yield this.data[index] as [K, V] - } - }, - *keys(): IterableIterator { - for (const key of indexMap.keys()) { - yield key - } - }, - *values(): IterableIterator { - for (const index of indexMap.values()) { - yield this.data[index]![1]! - } - }, - [Symbol.iterator]() { - return this.entries() - }, - get [Symbol.toStringTag]() { - return 'Map' - }, - toJSON(): Map { - return new Map([...indexMap].map(([k, v]) => [k, this.data[v]![1]!])) - }, - } - - const proxiedObject = proxy(vObject) - - Object.defineProperties(proxiedObject, { - size: { enumerable: false }, - data: { enumerable: false }, - nextIndex: { enumerable: false }, - toJSON: { enumerable: false }, - }) - - Object.seal(proxiedObject) - - return proxiedObject as unknown as Map & { - $$valtioSnapshot: Omit, 'set' | 'delete' | 'clear'> - } -} diff --git a/src/vanilla/utils/proxyMap-old.ts b/src/vanilla/utils/proxyMap-old.ts deleted file mode 100644 index 6fc3a9c0..00000000 --- a/src/vanilla/utils/proxyMap-old.ts +++ /dev/null @@ -1,111 +0,0 @@ -import { proxy } from '../../vanilla.ts' - -type KeyValRecord = [key: K, value: V] - -type InternalProxyMap = Map & { - data: KeyValRecord[] - toJSON: object -} - -/** - * proxyMap - * - * This is to create a proxy which mimic the native Map behavior. - * The API is the same as Map API - * - * @example - * import { proxyMap } from 'valtio/utils' - * const state = proxyMap([["key", "value"]]) - * - * //can be used inside a proxy as well - * const state = proxy({ - * count: 1, - * map: proxyMap() - * }) - * - * // When using an object as a key, you can wrap it with `ref` so it's not proxied - * // this is useful if you want to preserve the key equality - * import { ref } from 'valtio' - * - * const key = ref({}) - * state.set(key, "value") - * state.get(key) //value - * - * const key = {} - * state.set(key, "value") - * state.get(key) //undefined - */ -export function proxyMap(entries?: Iterable | null) { - const map: InternalProxyMap = proxy({ - data: Array.from(entries || []) as KeyValRecord[], - has(key) { - return this.data.some((p) => p[0] === key) - }, - set(key, value) { - const record = this.data.find((p) => p[0] === key) - if (record) { - record[1] = value - } else { - this.data.push([key, value]) - } - return this - }, - get(key) { - return this.data.find((p) => p[0] === key)?.[1] - }, - delete(key) { - const index = this.data.findIndex((p) => p[0] === key) - if (index === -1) { - return false - } - this.data.splice(index, 1) - return true - }, - clear() { - this.data.splice(0) - }, - get size() { - return this.data.length - }, - toJSON() { - return new Map(this.data) - }, - forEach(cb) { - this.data.forEach((p) => { - cb(p[1], p[0], this) - }) - }, - keys() { - return this.data.map((p) => p[0]).values() - }, - values() { - return this.data.map((p) => p[1]).values() - }, - entries() { - return new Map(this.data).entries() - }, - get [Symbol.toStringTag]() { - return 'Map' - }, - [Symbol.iterator]() { - return this.entries() - }, - }) - - Object.defineProperties(map, { - data: { - enumerable: false, - }, - size: { - enumerable: false, - }, - toJSON: { - enumerable: false, - }, - }) - Object.seal(map) - - return map as unknown as Map & { - $$valtioSnapshot: Omit, 'set' | 'delete' | 'clear'> - } -} diff --git a/src/vanilla/utils/proxyMap-rawMap1.ts b/src/vanilla/utils/proxyMap-rawMap1.ts deleted file mode 100644 index dc846264..00000000 --- a/src/vanilla/utils/proxyMap-rawMap1.ts +++ /dev/null @@ -1,195 +0,0 @@ -import { - proxy, - snapshot, - subscribe, - unstable_getInternalStates, -} from '../../vanilla.ts' - -const { proxyStateMap, snapCache } = unstable_getInternalStates() -const maybeProxify = (x: any) => (typeof x === 'object' ? proxy({ x }).x : x) -const isProxy = (x: any) => proxyStateMap.has(x) - -type InternalProxyObject = Map & { - epoch: number - _registerSnap: boolean - toJSON: () => Map -} - -export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { - const rawMap = new Map() - const unsubKeyMap = new WeakMap void>() - const unsubValMap = new WeakMap void>() - const snapMapCache = new WeakMap>() - const registerSnapMap = () => { - const cache = snapCache.get(vObject) - const latestSnap = cache?.[1] - if (latestSnap && !snapMapCache.has(latestSnap)) { - const snapMap = new Map() - for (const [k, v] of rawMap) { - snapMap.set( - isProxy(k) ? (snapshot(k as object) as K) : k, - isProxy(v) ? (snapshot(v as object) as V) : v, - ) - } - snapMapCache.set(latestSnap, snapMap) - return true - } - return false - } - const getSnapMap = (x: any) => snapMapCache.get(x) - - if (entries !== null && typeof entries !== 'undefined') { - if (typeof entries[Symbol.iterator] !== 'function') { - throw new TypeError( - 'proxyMap:\n\tinitial state must be iterable\n\t\ttip: structure should be [[key, value]]', - ) - } - for (const [k, v] of entries) { - const key = maybeProxify(k) - const value = maybeProxify(v) - rawMap.set(key, value) - } - } - - const vObject: InternalProxyObject = { - epoch: 0, - get _registerSnap() { - return registerSnapMap() - }, - get size() { - const map = getSnapMap(this) || rawMap - return map.size - }, - get(key: K) { - const map = getSnapMap(this) || rawMap - const k = maybeProxify(key) - if (!map.has(k)) { - if (!isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.epoch - } - return undefined - } - const val = map.get(k) as V - if (isProxy(this)) { - return val - } - if (isProxy(val)) { - return snapshot(val as object) as V - } - return val - }, - has(key: K) { - const map = getSnapMap(this) || rawMap - const k = maybeProxify(key) - const exists = map.has(k) - if (!exists && !isProxy(this)) { - // eslint-disable-next-line @typescript-eslint/no-unused-expressions - this.epoch - } - return exists - }, - set(key: K, value: V) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const v = maybeProxify(value) - if (rawMap.has(k)) { - const val = rawMap.get(k) as V - if (!Object.is(val, v)) { - unsubValMap.get(val as object)?.() - unsubValMap.delete(val as object) - if (isProxy(v)) { - unsubValMap.set( - v, - subscribe(v, () => void this.epoch++, true), - ) - } - this.epoch++ - rawMap.set(k, v) - } - return this - } - if (isProxy(k)) { - unsubKeyMap.set( - k, - subscribe(k, () => void this.epoch++, true), - ) - } - if (isProxy(v)) { - unsubValMap.set( - v, - subscribe(v, () => void this.epoch++, true), - ) - } - this.epoch++ - rawMap.set(k, v) - return this - }, - delete(key: K) { - const k = maybeProxify(key) - if (!rawMap.has(k)) { - return false - } - const val = rawMap.get(k) - const unsubKey = unsubKeyMap.get(k) - rawMap.delete(k) - const unsubVal = unsubValMap.get(val as object) - - unsubKey?.() - unsubVal?.() - this.epoch++ - return true - }, - clear() { - throw new Error('Not implemented') - }, - forEach(cb: (value: V, key: K, map: Map) => void) { - const map = getSnapMap(this) || rawMap - map.forEach(cb) - }, - *entries(): MapIterator<[K, V]> { - const map = getSnapMap(this) || rawMap - for (const [k, v] of map) { - yield [k, v] - } - }, - *keys(): IterableIterator { - const map = getSnapMap(this) || rawMap - for (const k of map.keys()) { - yield k - } - }, - *values(): IterableIterator { - const map = getSnapMap(this) || rawMap - for (const v of map.values()) { - yield v - } - }, - [Symbol.iterator]() { - return this.entries() - }, - get [Symbol.toStringTag]() { - return 'Map' - }, - toJSON(): Map { - const map = getSnapMap(this) || rawMap - return map - }, - } - - const proxiedObject = proxy(vObject) - - Object.defineProperties(proxiedObject, { - size: { enumerable: false }, - epoch: { enumerable: false }, - toJSON: { enumerable: false }, - }) - - Object.seal(proxiedObject) - - return proxiedObject as unknown as Map & { - $$valtioSnapshot: Omit, 'set' | 'delete' | 'clear'> - } -} diff --git a/src/vanilla/utils/proxyMap-tree1.ts b/src/vanilla/utils/proxyMap-tree1.ts deleted file mode 100644 index 84883e66..00000000 --- a/src/vanilla/utils/proxyMap-tree1.ts +++ /dev/null @@ -1,211 +0,0 @@ -import { proxy, unstable_getInternalStates } from '../../vanilla.ts' - -const { proxyStateMap } = unstable_getInternalStates() -const maybeProxify = (x: any) => (typeof x === 'object' ? proxy({ x }).x : x) -const isProxy = (x: any) => proxyStateMap.has(x) - -let nextKey = 0 -const objectKeyMap = new WeakMap() -const primitiveKeyMap = new Map() -const getKey = (x: unknown) => { - let key: number | undefined - if (typeof x === 'object' && x !== null) { - key = objectKeyMap.get(x) - if (key === undefined) { - key = nextKey++ - objectKeyMap.set(x as object, key) - } - } else { - key = primitiveKeyMap.get(x) - if (key === undefined) { - key = nextKey++ - primitiveKeyMap.set(x, key) - } - } - return key as number -} - -const TREE_BASE = 1000 - -type TreeNode = [ - keys: (number | undefined)[], - values: (unknown | undefined)[], - children: (TreeNode | undefined)[], - quotients: (number | undefined)[], -] - -const createNewTreeNode = (): TreeNode => [[], [], [], []] - -const insertIntoTreeNode = ( - node: TreeNode, - key: number, - value: unknown, - key2 = key, -): boolean => { - const [keys, values, children, quotients] = node - const index = key2 % TREE_BASE - const quotient = Math.floor(key2 / TREE_BASE) - if (keys[index] === key) { - values[index] = value - return false - } - let child = children[index] - if (keys[index] !== undefined && !child) { - child = createNewTreeNode() - insertIntoTreeNode(child, keys[index]!, values[index]!, quotients[index]!) - keys[index] = undefined - quotients[index] = undefined - values[index] = undefined - children[index] = child - } - if (child) { - return insertIntoTreeNode(child, key, value, quotient) - } - keys[index] = key - quotients[index] = quotient - values[index] = value - return true -} - -const EMPTY = Symbol() - -const searchFromTreeNode = (node: TreeNode, key: number, key2 = key) => { - const [keys, values, children] = node - const index = key2 % TREE_BASE - const quotient = Math.floor(key2 / TREE_BASE) - if (keys[index] === key) { - return values[index] - } - const child = children[index] - if (child) { - return searchFromTreeNode(child, key, quotient) - } - return EMPTY -} - -const deleteFromTreeNode = ( - _node: TreeNode, - key: number, - _key2 = key, -): boolean => { - throw new Error('Not implemented') -} - -type InternalProxyObject = Map & { - root: TreeNode - size: number - toJSON: () => Map -} - -export function proxyMap(entries?: Iterable<[K, V]> | undefined | null) { - const initialRoot = createNewTreeNode() - let initialSize = 0 - - if (entries !== null && typeof entries !== 'undefined') { - if (typeof entries[Symbol.iterator] !== 'function') { - throw new TypeError( - 'proxyMap:\n\tinitial state must be iterable\n\t\ttip: structure should be [[key, value]]', - ) - } - for (const [key, value] of entries) { - const k = maybeProxify(key) - const v = maybeProxify(value) - const added = insertIntoTreeNode(initialRoot, getKey(k), v) - if (added) { - initialSize++ - } - } - } - - const vObject: InternalProxyObject = { - root: initialRoot, - size: initialSize, - get(key: K) { - const k = maybeProxify(key) - const value = searchFromTreeNode(this.root, getKey(k)) - if (value === EMPTY) { - return undefined - } - return value as V - }, - has(key: K) { - const k = maybeProxify(key) - const value = searchFromTreeNode(this.root, getKey(k)) - return value !== EMPTY - }, - set(key: K, value: V) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const v = maybeProxify(value) - const added = insertIntoTreeNode(this.root, getKey(k), v) - if (added) { - this.size++ - } - return this - }, - delete(key: K) { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - const k = maybeProxify(key) - const deleted = deleteFromTreeNode(this.root, getKey(k)) - if (deleted) { - this.size-- - return true - } - return false - }, - clear() { - if (!isProxy(this)) { - throw new Error('Cannot perform mutations on a snapshot') - } - this.root = createNewTreeNode() - }, - forEach(_cb: (value: V, key: K, map: Map) => void) { - // indexMap.forEach((index) => { - // cb(this.data[index + 1] as V, this.data[index] as K, this) - // }) - }, - *entries(): MapIterator<[K, V]> { - //for (const index of indexMap.values()) { - // yield [this.data[index], this.data[index + 1]] as [K, V] - //} - }, - *keys(): IterableIterator { - // for (const key of indexMap.keys()) { - // yield key - // } - }, - *values(): IterableIterator { - // for (const index of indexMap.values()) { - // yield this.data[index + 1] as V - // } - }, - [Symbol.iterator]() { - return this.entries() - }, - get [Symbol.toStringTag]() { - return 'Map' - }, - toJSON(): Map { - // return new Map([...indexMap].map(([k, i]) => [k, this.data[i + 1] as V])) - return 'Not implemented' as never - }, - } - - const proxiedObject = proxy(vObject) - - Object.defineProperties(proxiedObject, { - size: { enumerable: false }, - data: { enumerable: false }, - toJSON: { enumerable: false }, - }) - - Object.seal(proxiedObject) - - return proxiedObject as unknown as Map & { - $$valtioSnapshot: Omit, 'set' | 'delete' | 'clear'> - } -} diff --git a/src/vanilla/utils/proxyMap-indexMap-keyval.ts b/src/vanilla/utils/proxyMap.ts similarity index 100% rename from src/vanilla/utils/proxyMap-indexMap-keyval.ts rename to src/vanilla/utils/proxyMap.ts diff --git a/src/vanilla/utils/test.ts b/src/vanilla/utils/test.ts deleted file mode 100644 index 822630cc..00000000 --- a/src/vanilla/utils/test.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { snapshot } from '../../vanilla.ts' -import { proxyMap } from './proxyMap-indexMap-keyval.ts' -import { proxySet } from './proxySet.ts' - -const state = proxySet(['val1', 'val2']) -const snap1 = snapshot(state) -console.log(snap1.has('val1'), 'true') -state.delete('val2') -const snap2 = snapshot(state) -console.log(snap1.has('val1'), 'true') -console.log(snap1.has('val2'), 'true') -console.log(snap2.has('val1'), 'true') -console.log(snap2.has('val2'), 'false') diff --git a/tests/proxyMap.bench.ts b/tests/proxyMap.bench.ts index 60ce1145..a9ea8288 100644 --- a/tests/proxyMap.bench.ts +++ b/tests/proxyMap.bench.ts @@ -1,7 +1,7 @@ /* eslint-disable vitest/consistent-test-it */ import { bench, describe, test } from 'vitest' import { snapshot } from 'valtio' -import { newProxyMapKeyVals, proxyMapRawMap } from 'valtio/utils' +import { proxyMap } from 'valtio/utils' // Helper function to generate test data function generateTestData(size: number): [number, number][] { @@ -18,22 +18,8 @@ TEST_SIZES.forEach((size) => { describe(`Insertion -${size} items`, () => { const testData = generateTestData(size) - // bench('Native proxyMap', () => { - // const map = new Map() - // testData.forEach(([key, value]) => { - // map.set(key, value) - // }) - // }) - - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals() - testData.forEach(([key, value]) => { - map.set(key, value) - }) - }) - - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap() + bench('proxyMap', () => { + const map = proxyMap() testData.forEach(([key, value]) => { map.set(key, value) }) @@ -43,16 +29,8 @@ TEST_SIZES.forEach((size) => { describe(`Insertion and Update -${size} items`, () => { const testData = generateTestData(size) - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals() - testData.forEach(([key, value]) => { - map.set(key, value) - map.set(key, -1) - }) - }) - - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap() + bench('proxyMap', () => { + const map = proxyMap() testData.forEach(([key, value]) => { map.set(key, value) map.set(key, -1) @@ -63,22 +41,8 @@ TEST_SIZES.forEach((size) => { describe(`Retrieval -${size} items`, () => { const testData = generateTestData(size) - // bench('Native Map', () => { - // const map = new Map(testData) - // testData.forEach(([key]) => { - // map.get(key) - // }) - // }) - - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals(testData) - testData.forEach(([key]) => { - map.get(key) - }) - }) - - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap(testData) + bench('proxyMap', () => { + const map = proxyMap(testData) testData.forEach(([key]) => { map.get(key) }) @@ -88,22 +52,8 @@ TEST_SIZES.forEach((size) => { describe(`Deletion -${size} items`, () => { const testData = generateTestData(size) - // bench('Native Map', () => { - // const map = new Map(testData) - // testData.forEach(([key]) => { - // map.delete(key) - // }) - // }) - - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals(testData) - testData.forEach(([key]) => { - map.delete(key) - }) - }) - - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap(testData) + bench('proxyMap', () => { + const map = proxyMap(testData) testData.forEach(([key]) => { map.delete(key) }) @@ -113,45 +63,16 @@ TEST_SIZES.forEach((size) => { describe(`Iteration -${size} items`, () => { const testData = generateTestData(size) - // bench('Native Map', () => { - // const map = new Map(testData) - // testData.forEach(([key, value]) => {}) - // }) - - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals(testData) - testData.forEach(([key, value]) => {}) - }) - - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap(testData) + bench('proxyMap', () => { + const map = proxyMap(testData) testData.forEach(([key, value]) => {}) }) }) describe(`Insertion, Retrieval, and Deletion -${size} items`, () => { const testData = generateTestData(size) - - // bench('New proxyMapKeyVals', () => { - // const map = new Map(testData) - // testData.forEach(([key, value]) => { - // map.set(key, value) - // map.get(key) - // map.delete(key) - // }) - // }) - - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals(testData) - testData.forEach(([key, value]) => { - map.set(key, value) - map.get(key) - map.delete(key) - }) - }) - - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap(testData) + bench('proxyMap', () => { + const map = proxyMap(testData) testData.forEach(([key, value]) => { map.set(key, value) map.get(key) @@ -163,16 +84,8 @@ TEST_SIZES.forEach((size) => { describe(`entries -${size} items`, () => { const testData = generateTestData(size) - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals(testData) - for (const [k, v] of map.entries()) { - const _k = k - const _v = v - } - }) - - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap(testData) + bench('proxyMap', () => { + const map = proxyMap(testData) for (const [k, v] of map.entries()) { const _k = k const _v = v @@ -183,15 +96,8 @@ TEST_SIZES.forEach((size) => { describe(`keys -${size} items`, () => { const testData = generateTestData(size) - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals(testData) - for (const k of map.keys()) { - const _k = k - } - }) - - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap(testData) + bench('proxyMap', () => { + const map = proxyMap(testData) for (const k of map.keys()) { const _k = k } @@ -201,14 +107,8 @@ TEST_SIZES.forEach((size) => { describe(`values -${size} items`, () => { const testData = generateTestData(size) - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals(testData) - for (const v of map.values()) { - const _v = v - } - }) - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap(testData) + bench('proxyMap', () => { + const map = proxyMap(testData) for (const v of map.values()) { const _v = v } @@ -218,16 +118,8 @@ TEST_SIZES.forEach((size) => { describe(`snapshot -${size} items`, () => { const testData = generateTestData(size) - bench('New proxyMapKeyVals', () => { - const map = newProxyMapKeyVals(testData) - const snap = snapshot(map) - testData.forEach(([key, value]) => { - snap.get(key) - }) - }) - - bench('proxyMap Raw Map', () => { - const map = proxyMapRawMap(testData) + bench('proxyMap', () => { + const map = proxyMap(testData) const snap = snapshot(map) testData.forEach(([key, value]) => { snap.get(key)