Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New implementation of proxyMap and proxySet with performance improvements #965

Merged
merged 170 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
170 commits
Select commit Hold shift + click to select a range
f7ab143
pushing up to get more eyes on it
overthemike Sep 4, 2024
8ade601
added test for snapshot
overthemike Sep 4, 2024
b13d580
taking a break
overthemike Sep 4, 2024
6ce93c9
taking a break
overthemike Sep 4, 2024
5d7690e
removing extra set methods for now
overthemike Sep 4, 2024
c66c4b2
sealed proxied obects
overthemike Sep 4, 2024
fa2be06
got all tests to pass
overthemike Sep 4, 2024
2b467bc
got rid of comma
overthemike Sep 4, 2024
122ca59
got rid of set tests in utils folder
overthemike Sep 4, 2024
dbfafa2
update version after update
overthemike Sep 4, 2024
d6a2f3a
updated to allow values to be keys
overthemike Sep 4, 2024
bf082f3
updated to allow values to be keys
overthemike Sep 4, 2024
1795e74
updated package.json scripts
overthemike Sep 5, 2024
2db267c
2.0.1
overthemike Sep 5, 2024
02da182
changing brances so storing attempts
overthemike Sep 12, 2024
e776ccf
updates
overthemike Sep 24, 2024
feb9578
fixed get
overthemike Sep 24, 2024
62f89d0
added return type to fix types on snapshot
overthemike Sep 25, 2024
bdfceac
added this.data.length in has method
overthemike Sep 25, 2024
79fc0f1
updated has to only show this.data.map if key doesn't exist
overthemike Sep 25, 2024
1bab946
updated has to only show this.data.map if key doesn't exist
overthemike Sep 25, 2024
5112f4f
changed has to check if it's in a snapshot
overthemike Sep 25, 2024
bdaff93
remove getVersion - doesn't work
overthemike Sep 25, 2024
b79ae22
added back in canProxy
overthemike Sep 25, 2024
fe6e3c0
imported canProxy from valtio
overthemike Sep 25, 2024
472dfbb
removed unneeded functions and checks that are done in valtio core
overthemike Sep 25, 2024
c140943
changed canProxy
overthemike Sep 25, 2024
28ea00b
pushing up for proxyMap testing
overthemike Oct 1, 2024
5ad1f91
removed unused file
overthemike Oct 1, 2024
6e0e55b
updated proxyMap to use sparse array
overthemike Oct 5, 2024
0de7dca
removed use of internal map
overthemike Oct 5, 2024
475d3eb
added proxySet
overthemike Oct 5, 2024
edfa3cd
moved proxySet to be the main one
overthemike Oct 5, 2024
9006398
added old proxySet in for reference
overthemike Oct 5, 2024
2b7e4d5
fixed proxySet
overthemike Oct 5, 2024
7f4b654
made proxyMap get set and delete consistent
overthemike Oct 5, 2024
8552805
better syntax for maybeProxify
overthemike Oct 5, 2024
ecb899c
added benchmarks for proxyMap
overthemike Oct 5, 2024
0921f94
added benchmarks for proxySet
overthemike Oct 5, 2024
e45c6cd
removed deno from lock file
overthemike Oct 5, 2024
07ca2af
removed all test files
overthemike Oct 6, 2024
6f5441a
updated maybeproxify in proxySet
overthemike Oct 6, 2024
0ec88a0
fixed conflicts
overthemike Oct 6, 2024
9c03b50
pnpm frozen lockfile
overthemike Oct 6, 2024
0a73caa
change autoinstall peers to true in pnpm-lock
overthemike Oct 6, 2024
3b412c1
changed vitest.config.mts to ts
overthemike Oct 6, 2024
92ba0bf
changed version number back and updated import paths to be relative
overthemike Oct 6, 2024
58e4db0
fixed toJSON to remain in order and proxified keys in .has() for prox…
overthemike Oct 8, 2024
74f4806
tested set vs filter on empty slots
overthemike Oct 8, 2024
0446d5a
fixed pnpm lock file
overthemike Oct 8, 2024
bf00185
ran pnpm install
overthemike Oct 8, 2024
6fcb3cb
auto-install-peers fix
overthemike Oct 8, 2024
74f0ad8
added isSnapshot function for better readability
overthemike Oct 8, 2024
d720a96
removed unused package
overthemike Oct 9, 2024
8354c71
removed linked package in package.json
overthemike Oct 9, 2024
285f1a9
fixed lock file
overthemike Oct 9, 2024
4dbd796
changed to isProxy with this context
overthemike Oct 9, 2024
b5f253d
Update src/vanilla/utils/proxyMap.ts
overthemike Oct 9, 2024
99dd235
pushing up for testing
overthemike Oct 10, 2024
615f476
pushing up for testing
overthemike Oct 10, 2024
0b93c32
fixed delete, has and get
overthemike Oct 10, 2024
2c85863
fixed delete, has and get
overthemike Oct 10, 2024
416d319
uploading newest changes
overthemike Oct 11, 2024
d147acf
uploading newest changes
overthemike Oct 11, 2024
8528ec9
reverting
overthemike Oct 12, 2024
2eef414
fixed import in test file
overthemike Oct 12, 2024
5701700
updated to use nextIndex
overthemike Oct 12, 2024
034d3d3
put nextIndex on the object itself
overthemike Oct 12, 2024
b3fd463
added undefined values to initialization
overthemike Oct 12, 2024
8e5909a
updated entries check
overthemike Oct 12, 2024
52f7afe
changed nextIndex to use local var
overthemike Oct 12, 2024
caac109
cached indexMap.has to index
overthemike Oct 12, 2024
99fdb0a
removed unneeded index.has call
overthemike Oct 12, 2024
e9a0510
added this.nextIndex
overthemike Oct 12, 2024
c64e9a0
replaced this.data.length with this.nextIndex
overthemike Oct 12, 2024
bd6cbf3
refactor
dai-shi Oct 12, 2024
01f8d05
updated initial data and initial next data
overthemike Oct 12, 2024
ecc154e
added filled proxyMap and new benchmark
overthemike Oct 12, 2024
91206eb
benchmark minSize
dai-shi Oct 12, 2024
98597bb
Revert "benchmark minSize"
dai-shi Oct 12, 2024
7637dc1
compare 3
dai-shi Oct 12, 2024
4728940
compare 3
dai-shi Oct 12, 2024
babd153
keyVal
dai-shi Oct 12, 2024
05ea79d
new keyval
dai-shi Oct 12, 2024
c90558a
minor optimization
dai-shi Oct 12, 2024
ebaa5f9
added MIN_DATA_SIZE constant
overthemike Oct 13, 2024
697c505
move keyval
dai-shi Oct 13, 2024
8039eff
revert some changes
dai-shi Oct 13, 2024
6a40725
Merge branch 'keyed-collections' into keyed-collections-2
dai-shi Oct 13, 2024
8720c85
rename
dai-shi Oct 13, 2024
aa6ccc2
removed old benchmarks and added vitest benchmarks for proxyMap
overthemike Oct 13, 2024
0f71f73
fixed lock file
overthemike Oct 13, 2024
6fa4bff
added ts extension for older versions of ts in test file
overthemike Oct 13, 2024
8d4e6bf
changed MapIterator to IterableIterator
overthemike Oct 13, 2024
815715e
no longer importing with .ts extension
overthemike Oct 13, 2024
8c749e2
no longer importing with .ts extension
overthemike Oct 13, 2024
fb29830
proxySet changes and added keyvals solution
overthemike Oct 13, 2024
8ef03de
removed ts extension from imports in proxySet and proxyMap-indexMap-k…
overthemike Oct 13, 2024
c1a5611
fixed toJSON in proxySet
overthemike Oct 13, 2024
1c4cc2b
change MapIterator to IterableIterator
overthemike Oct 13, 2024
2468785
Merge branch 'keyed-collections' into keyed-collections-2
dai-shi Oct 13, 2024
8318e5b
add tree1 impl
dai-shi Oct 13, 2024
af47ef4
got lock file from main
overthemike Oct 13, 2024
4b8b6bf
fixing lock file
overthemike Oct 13, 2024
9147968
added ts extensions backf
overthemike Oct 13, 2024
f4337b4
removed test example
overthemike Oct 13, 2024
efa3996
extensions added back
overthemike Oct 13, 2024
9caeadd
recreating lockfile
overthemike Oct 13, 2024
62ff2c9
reverting changes for website lockfile and todo-with-proxyMap lockfile
overthemike Oct 13, 2024
0924632
removed ts extension from proxyMap.bench.ts
overthemike Oct 13, 2024
c03b684
reverted changes for lockfile and package.json
overthemike Oct 13, 2024
83eef9b
removed old btree implementation
overthemike Oct 13, 2024
1dfe0b3
changed MapIterator to IterableIterator
overthemike Oct 13, 2024
074e7ad
incomplete rawMap impl
dai-shi Oct 13, 2024
507ce71
a minor fix
dai-shi Oct 14, 2024
dc7c3b0
Merge branch 'keyed-collections' into keyed-collections-2
dai-shi Oct 14, 2024
5ecb453
fix?
dai-shi Oct 14, 2024
18db486
export them properly
dai-shi Oct 14, 2024
901ae13
rename a little bit
dai-shi Oct 14, 2024
d2aad53
revert default
dai-shi Oct 14, 2024
10b455d
fixed size issue
overthemike Oct 14, 2024
7101f93
Merge remote-tracking branch 'valtio/keyed-collections-2' into keyed-…
overthemike Oct 14, 2024
9d0275b
removed unneeded copy of key-vals
overthemike Oct 14, 2024
4295c12
snap map
dai-shi Oct 14, 2024
d2d2d14
Merge branch 'keyed-collections-2' into keyed-collections
overthemike Oct 14, 2024
5f0bcb0
fixed conflict
overthemike Oct 14, 2024
5334e53
just to avoid warnings
dai-shi Oct 14, 2024
fc4747e
an improvement to rawMap1, not sure if it works
dai-shi Oct 14, 2024
d213154
Merge branch 'keyed-collections-2' into keyed-collections
overthemike Oct 14, 2024
5acaed0
enumerable size
dai-shi Oct 14, 2024
def8ca6
Revert "enumerable size"
dai-shi Oct 14, 2024
db1fef6
hack with _registerSnap
dai-shi Oct 14, 2024
29f325e
Merge branch 'keyed-collections-2' into keyed-collections
overthemike Oct 14, 2024
18f2679
added benchmarks
overthemike Oct 14, 2024
607b2e1
do not enumerate index
dai-shi Oct 14, 2024
f22f3cc
added snap registry to keyval
overthemike Oct 14, 2024
a009289
fixed index check
overthemike Oct 14, 2024
37debaa
added snap awareness to proxySet
overthemike Oct 14, 2024
e2b3f1e
added snap test to proxySet
overthemike Oct 14, 2024
0133712
Merge branch 'keyed-collections' into keyed-collections-2
dai-shi Oct 14, 2024
f9645fb
fix
dai-shi Oct 14, 2024
5d4ae39
removed variations
overthemike Oct 14, 2024
b5d6f3b
removed subscribes in proxyMap
overthemike Oct 14, 2024
2162f55
Merge branch 'keyed-collections' into keyed-collections-2
dai-shi Oct 14, 2024
e772799
recover tree1 -> tree2
dai-shi Oct 14, 2024
f29aa6c
fix a bug
dai-shi Oct 14, 2024
d1ae9d6
added old file for reference
overthemike Oct 14, 2024
655bf97
fixed conflict
overthemike Oct 14, 2024
3b459d6
tree2 impl
dai-shi Oct 14, 2024
df4a180
refactor
dai-shi Oct 14, 2024
59768aa
bench tree2
dai-shi Oct 14, 2024
33835af
tests
overthemike Oct 14, 2024
0957ce7
Merge branch 'keyed-collections-2' into keyed-collections
overthemike Oct 14, 2024
febc310
updated proxySet
overthemike Oct 14, 2024
98ae310
Merge branch 'main' into keyed-collections
overthemike Oct 14, 2024
5759902
refactor proxyMap&Set
dai-shi Oct 15, 2024
ffe6b72
removed test file
overthemike Oct 15, 2024
0de5cbe
Merge branch 'keyed-collections-2' into keyed-collections
overthemike Oct 15, 2024
f1fc6b8
merged branch and got to merge status
overthemike Oct 15, 2024
a758e14
removed todo comment
overthemike Oct 15, 2024
a66fad2
added review suggestions
overthemike Oct 15, 2024
00892b8
added review suggestions for proxySet as well
overthemike Oct 15, 2024
849d33f
Merge branch 'main' into keyed-collections
overthemike Oct 15, 2024
3faa841
avoid maybeProxify
dai-shi Oct 15, 2024
016f590
no need to add key in this data
dai-shi Oct 15, 2024
b0e4b3e
removed isProxy check on has calls
overthemike Oct 15, 2024
dbcc0ac
merged in changes
overthemike Oct 15, 2024
b001c7e
removed maybeProxify from proxySet
overthemike Oct 15, 2024
7e0b6e8
added back in maybeProxify to proxySet
overthemike Oct 15, 2024
6ee95c7
refactor
dai-shi Oct 16, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
202 changes: 120 additions & 82 deletions src/vanilla/utils/proxyMap.ts
Original file line number Diff line number Diff line change
@@ -1,111 +1,149 @@
import { proxy } from '../../vanilla.ts'
import { proxy, unstable_getInternalStates } from '../../vanilla.ts'

type KeyValRecord<K, V> = [key: K, value: V]
const { proxyStateMap, snapCache } = unstable_getInternalStates()
const isProxy = (x: any) => proxyStateMap.has(x)

type InternalProxyMap<K, V> = Map<K, V> & {
data: KeyValRecord<K, V>[]
toJSON: object
type InternalProxyObject<K, V> = Map<K, V> & {
data: Array<V>
index: number
toJSON: () => Map<K, V>
}

/**
* 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<K, V>(entries?: Iterable<readonly [K, V]> | null) {
const map: InternalProxyMap<K, V> = proxy({
data: Array.from(entries || []) as KeyValRecord<K, V>[],
has(key) {
return this.data.some((p) => p[0] === key)
export function proxyMap<K, V>(entries?: Iterable<[K, V]> | undefined | null) {
const initialData: Array<V> = []
let initialIndex = 0
const indexMap = new Map<K, number>()

const snapMapCache = new WeakMap<object, Map<K, number>>()
const registerSnapMap = () => {
const cache = snapCache.get(vObject)
const latestSnap = cache?.[1]
if (latestSnap && !snapMapCache.has(latestSnap)) {
const clonedMap = new Map(indexMap)
snapMapCache.set(latestSnap, clonedMap)
}
}
const getMapForThis = (x: any) => snapMapCache.get(x) || indexMap

if (entries) {
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) {
indexMap.set(key, initialIndex)
initialData[initialIndex++] = value
}
}

const vObject: InternalProxyObject<K, V> = {
data: initialData,
index: initialIndex,
get size() {
if (!isProxy(this)) {
registerSnapMap()
}
const map = getMapForThis(this)
return map.size
},
get(key: K) {
const map = getMapForThis(this)
const index = map.get(key)
if (index === undefined) {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
this.index // touch property for tracking
return undefined
}
return this.data[index]
},
has(key: K) {
const map = getMapForThis(this)
const exists = map.has(key)
if (!exists) {
// eslint-disable-next-line @typescript-eslint/no-unused-expressions
this.index // touch property for tracking
}
return exists
},
set(key, value) {
const record = this.data.find((p) => p[0] === key)
if (record) {
record[1] = value
set(key: K, value: V) {
if (!isProxy(this)) {
throw new Error('Cannot perform mutations on a snapshot')
}
const index = indexMap.get(key)
if (index === undefined) {
indexMap.set(key, this.index)
this.data[this.index++] = value
} else {
this.data.push([key, value])
this.data[index] = 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) {
delete(key: K) {
if (!isProxy(this)) {
throw new Error('Cannot perform mutations on a snapshot')
}
const index = indexMap.get(key)
if (index === undefined) {
return false
}
this.data.splice(index, 1)
delete this.data[index]
indexMap.delete(key)
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)
if (!isProxy(this)) {
throw new Error('Cannot perform mutations on a snapshot')
}
this.data.length = 0 // empty array
this.index = 0
indexMap.clear()
},
forEach(cb: (value: V, key: K, map: Map<K, V>) => void) {
const map = getMapForThis(this)
map.forEach((index, key) => {
cb(this.data[index]!, key, this)
})
},
keys() {
return this.data.map((p) => p[0]).values()
},
values() {
return this.data.map((p) => p[1]).values()
*entries(): MapIterator<[K, V]> {
const map = getMapForThis(this)
for (const [key, index] of map) {
yield [key, this.data[index]!]
}
},
entries() {
return new Map(this.data).entries()
*keys(): IterableIterator<K> {
const map = getMapForThis(this)
for (const key of map.keys()) {
yield key
}
},
get [Symbol.toStringTag]() {
return 'Map'
*values(): IterableIterator<V> {
const map = getMapForThis(this)
for (const index of map.values()) {
yield this.data[index]!
}
},
[Symbol.iterator]() {
return this.entries()
},
})

Object.defineProperties(map, {
data: {
enumerable: false,
},
size: {
enumerable: false,
get [Symbol.toStringTag]() {
return 'Map'
},
toJSON: {
enumerable: false,
toJSON(): Map<K, V> {
return new Map(this.entries())
},
}

const proxiedObject = proxy(vObject)
Object.defineProperties(proxiedObject, {
size: { enumerable: false },
index: { enumerable: false },
data: { enumerable: false },
toJSON: { enumerable: false },
})
Object.seal(map)
Object.seal(proxiedObject)

return map as unknown as Map<K, V> & {
return proxiedObject as unknown as Map<K, V> & {
$$valtioSnapshot: Omit<Map<K, V>, 'set' | 'delete' | 'clear'>
}
}
Loading