-
-
Notifications
You must be signed in to change notification settings - Fork 83
Expand file tree
/
Copy pathcache.ts
More file actions
162 lines (138 loc) · 3.77 KB
/
Copy pathcache.ts
File metadata and controls
162 lines (138 loc) · 3.77 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
/**
* An extended `WeakMap` that can also hold regular keys.
* (in those cases, it behaves like a regular `Map` and does not free memory)
*
* An additional `clear` method is added to clear the regular map.
*/
export class MaybeWeakMap<K, V> implements WeakMap<any, V> {
private _map: Map<K, V>
private _weakMap: WeakMap<any, V>
constructor() {
this._map = new Map()
this._weakMap = new WeakMap()
}
[Symbol.toStringTag]: string = 'MaybeWeakMap'
delete(key: K): boolean {
if (this._weakMap.has(key))
return this._weakMap.delete(key)
return this._map.delete(key)
}
has(key: K): boolean {
if (this._weakMap.has(key))
return true
return this._map.has(key)
}
set(key: K, value: V): this {
if (this._weakMap.has(key))
this._weakMap.set(key, value)
else
this._map.set(key, value)
return this
}
get(key: K): V | undefined {
if (this._weakMap.has(key))
return this._weakMap.get(key)
return this._map.get(key)
}
clear(): void {
this._map.clear()
}
}
/**
* A `Map` that accepts an array of keys, treating it as a tuple.
*/
export class FixedTupleMap<K extends readonly any[], V> {
private _mapTree: Map<any, any>
constructor(
public readonly length: K['length'],
) {
this._mapTree = new Map()
}
private _getLastMap(key: K): Map<any, any> {
let map = this._mapTree
for (const k of key.slice(0, -1)) {
if (!map.has(k)) {
map.set(k, new Map())
}
map = map.get(k) as Map<any, any>
}
return map
}
set(key: K, value: V): this {
if (key.length !== this.length) {
throw new Error(`Expect tuple of length ${this.length}, got ${key.length}`)
}
const lastMap = this._getLastMap(key)
lastMap.set(key[key.length - 1], value)
return this
}
get(key: K): V | undefined {
const lastMap = this._getLastMap(key)
return lastMap.get(key[key.length - 1])
}
clear(): void {
this._mapTree.clear()
}
delete(key: K): boolean {
const lastMap = this._getLastMap(key)
return lastMap.delete(key[key.length - 1])
}
get size(): number {
return this._mapTree.size
}
* _traverseMap() {
function* traverse(map: Map<any, any>, keys: any[], depthLeft: number): Generator<{ keys: any[], map: Map<any, V> }> {
if (depthLeft === 0) {
yield { keys, map }
}
else {
for (const [key, value] of map.entries()) {
yield* traverse(value, [...keys, key], depthLeft - 1)
}
}
}
yield* traverse(this._mapTree, [], this.length)
}
}
export class TupleMap<K extends readonly any[], V> {
private _lengthMap: Map<number, FixedTupleMap<K, V>>
constructor() {
this._lengthMap = new Map()
}
get(key: K): V | undefined {
const length = key.length
const map = this._lengthMap.get(length)
return map?.get(key)
}
set(key: K, value: V): this {
const length = key.length
let map = this._lengthMap.get(length)
if (!map) {
map = new FixedTupleMap(length)
this._lengthMap.set(length, map)
}
map.set(key, value)
return this
}
delete(key: K): boolean {
const length = key.length
const map = this._lengthMap.get(length)
return map?.delete(key) ?? false
}
clear(): void {
this._lengthMap.clear()
}
}
export function makeCachedFunction<T extends (...args: any[]) => any>(fn: T): T & { cache: TupleMap<Parameters<T>, ReturnType<T>> } {
const cache = new TupleMap<Parameters<T>, ReturnType<T>>()
const wrapper = function (this: ThisType<T>, ...args: Parameters<T>) {
let result = cache.get(args)
if (result)
return result
result = fn(...args)
cache.set(args, result!)
return result
}
wrapper.cache = cache
return wrapper as T & { cache: TupleMap<Parameters<T>, ReturnType<T>> }
}