@contember/utilities 2.0.0-alpha.16 → 2.0.0-alpha.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/development/index.cjs +6 -0
- package/dist/development/index.cjs.map +1 -1
- package/dist/development/index.js +6 -0
- package/dist/development/index.js.map +1 -1
- package/dist/development/src/structures/BijectiveIndexedMap.cjs +116 -0
- package/dist/development/src/structures/BijectiveIndexedMap.cjs.map +1 -0
- package/dist/development/src/structures/BijectiveIndexedMap.js +116 -0
- package/dist/development/src/structures/BijectiveIndexedMap.js.map +1 -0
- package/dist/development/src/structures/LRUCache.cjs +32 -0
- package/dist/development/src/structures/LRUCache.cjs.map +1 -0
- package/dist/development/src/structures/LRUCache.js +32 -0
- package/dist/development/src/structures/LRUCache.js.map +1 -0
- package/dist/development/src/structures/WeakIdCache.cjs +30 -0
- package/dist/development/src/structures/WeakIdCache.cjs.map +1 -0
- package/dist/development/src/structures/WeakIdCache.js +30 -0
- package/dist/development/src/structures/WeakIdCache.js.map +1 -0
- package/dist/production/index.cjs +6 -0
- package/dist/production/index.cjs.map +1 -1
- package/dist/production/index.js +6 -0
- package/dist/production/index.js.map +1 -1
- package/dist/production/src/structures/BijectiveIndexedMap.cjs +116 -0
- package/dist/production/src/structures/BijectiveIndexedMap.cjs.map +1 -0
- package/dist/production/src/structures/BijectiveIndexedMap.js +116 -0
- package/dist/production/src/structures/BijectiveIndexedMap.js.map +1 -0
- package/dist/production/src/structures/LRUCache.cjs +32 -0
- package/dist/production/src/structures/LRUCache.cjs.map +1 -0
- package/dist/production/src/structures/LRUCache.js +32 -0
- package/dist/production/src/structures/LRUCache.js.map +1 -0
- package/dist/production/src/structures/WeakIdCache.cjs +30 -0
- package/dist/production/src/structures/WeakIdCache.cjs.map +1 -0
- package/dist/production/src/structures/WeakIdCache.js +30 -0
- package/dist/production/src/structures/WeakIdCache.js.map +1 -0
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/structures/BijectiveIndexedMap.d.ts +24 -0
- package/dist/types/structures/BijectiveIndexedMap.d.ts.map +1 -0
- package/dist/types/structures/LRUCache.d.ts +9 -0
- package/dist/types/structures/LRUCache.d.ts.map +1 -0
- package/dist/types/structures/WeakIdCache.d.ts +9 -0
- package/dist/types/structures/WeakIdCache.d.ts.map +1 -0
- package/dist/types/structures/index.d.ts +4 -0
- package/dist/types/structures/index.d.ts.map +1 -0
- package/dist/types/tsconfig.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -0
- package/src/structures/BijectiveIndexedMap.ts +137 -0
- package/src/structures/LRUCache.ts +29 -0
- package/src/structures/WeakIdCache.ts +25 -0
- package/src/structures/index.ts +3 -0
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
// Bijective as in we need to supply the inverse and also guarantee that the values are unique.
|
|
2
|
+
// Provided this invariant, this should behave more or less like a regular map except it has a somewhat special
|
|
3
|
+
// iteration order. Unlike a regular map, which just honors insertion order, this also provides a special method
|
|
4
|
+
// changeKey. With this, we can change the key under which a value (that is already on the map) is accessible.
|
|
5
|
+
// With an ordinary map, that operation would require us to delete the original [K1, V] pair and insert
|
|
6
|
+
// a new one [K2, V]. That would however change the order of iteration. This main purpose of this data structure is
|
|
7
|
+
// the ability to avoid that and enable the value V to preserve its original place.
|
|
8
|
+
export class BijectiveIndexedMap<K, V> implements Map<K, V> {
|
|
9
|
+
private readonly valueStore: Map<number, V>
|
|
10
|
+
private readonly index: Map<K, number>
|
|
11
|
+
private indexSeed: number
|
|
12
|
+
|
|
13
|
+
private readonly inverse: (value: V) => K
|
|
14
|
+
|
|
15
|
+
public readonly [Symbol.toStringTag] = 'BijectiveIndexedMap'
|
|
16
|
+
|
|
17
|
+
public constructor(originalMap: BijectiveIndexedMap<K, V>)
|
|
18
|
+
public constructor(inverse: (value: V) => K)
|
|
19
|
+
public constructor(inverseOrMap: BijectiveIndexedMap<K, V> | ((value: V) => K)) {
|
|
20
|
+
if (inverseOrMap instanceof BijectiveIndexedMap) {
|
|
21
|
+
this.valueStore = new Map(inverseOrMap.valueStore)
|
|
22
|
+
this.index = new Map(inverseOrMap.index)
|
|
23
|
+
this.indexSeed = inverseOrMap.indexSeed
|
|
24
|
+
this.inverse = inverseOrMap.inverse
|
|
25
|
+
} else {
|
|
26
|
+
this.valueStore = new Map()
|
|
27
|
+
this.index = new Map()
|
|
28
|
+
this.indexSeed = 0
|
|
29
|
+
this.inverse = inverseOrMap
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
private getNewIndex() {
|
|
34
|
+
return this.indexSeed++
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
public get size(): number {
|
|
38
|
+
return this.valueStore.size
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
public clear(): void {
|
|
42
|
+
this.valueStore.clear()
|
|
43
|
+
this.index.clear()
|
|
44
|
+
}
|
|
45
|
+
public delete(key: K): boolean {
|
|
46
|
+
const index = this.index.get(key)
|
|
47
|
+
|
|
48
|
+
if (index === undefined) {
|
|
49
|
+
return false
|
|
50
|
+
}
|
|
51
|
+
this.valueStore.delete(index)
|
|
52
|
+
this.index.delete(key)
|
|
53
|
+
return true
|
|
54
|
+
}
|
|
55
|
+
public forEach(callback: (value: V, key: K, map: Map<K, V>) => void, thisArg?: any): void {
|
|
56
|
+
for (const [key, value] of this.entries()) {
|
|
57
|
+
callback.call(thisArg, value, key, this)
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
public get(key: K): V | undefined {
|
|
62
|
+
const index = this.index.get(key)
|
|
63
|
+
|
|
64
|
+
if (index === undefined) {
|
|
65
|
+
return undefined
|
|
66
|
+
}
|
|
67
|
+
return this.valueStore.get(index)
|
|
68
|
+
}
|
|
69
|
+
public has(key: K): boolean {
|
|
70
|
+
return this.index.has(key)
|
|
71
|
+
}
|
|
72
|
+
public set(key: K, value: V): this {
|
|
73
|
+
const existingIndex = this.index.get(key)
|
|
74
|
+
|
|
75
|
+
if (existingIndex === undefined) {
|
|
76
|
+
const newIndex = this.getNewIndex()
|
|
77
|
+
this.index.set(key, newIndex)
|
|
78
|
+
this.valueStore.set(newIndex, value)
|
|
79
|
+
} else {
|
|
80
|
+
this.valueStore.set(existingIndex, value)
|
|
81
|
+
}
|
|
82
|
+
return this
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// This doesn't use the inverse in order to avoid confusion.
|
|
86
|
+
// See the description above.
|
|
87
|
+
public changeKey(oldKey: K, newKey: K): this {
|
|
88
|
+
const index = this.index.get(oldKey)
|
|
89
|
+
if (index === undefined) {
|
|
90
|
+
throw new Error()
|
|
91
|
+
}
|
|
92
|
+
this.index.delete(oldKey)
|
|
93
|
+
this.index.set(newKey, index)
|
|
94
|
+
return this
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
public changeKeyOrder(newOrder: Iterable<K>) {
|
|
98
|
+
for (const key of newOrder) {
|
|
99
|
+
const keyIndex = this.index.get(key)
|
|
100
|
+
if (keyIndex === undefined) {
|
|
101
|
+
throw new Error()
|
|
102
|
+
}
|
|
103
|
+
const value = this.valueStore.get(keyIndex)!
|
|
104
|
+
|
|
105
|
+
// This effectively removes value from wherever it was and puts it at the end.
|
|
106
|
+
// Provided newOrder.size is the same as valueStore, by the end of this loop
|
|
107
|
+
// the values will be in the correct order.
|
|
108
|
+
this.valueStore.delete(keyIndex)
|
|
109
|
+
this.valueStore.set(keyIndex, value)
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
public *[Symbol.iterator](): IterableIterator<[K, V]> {
|
|
114
|
+
yield* this.entries()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public *entries(): IterableIterator<[K, V]> {
|
|
118
|
+
for (const value of this.values()) {
|
|
119
|
+
yield [this.inverse(value), value]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
public *keys(): IterableIterator<K> {
|
|
124
|
+
for (const value of this.values()) {
|
|
125
|
+
yield this.inverse(value)
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
public *values(): IterableIterator<V> {
|
|
130
|
+
for (const value of this.valueStore.values()) {
|
|
131
|
+
if (value === undefined) {
|
|
132
|
+
continue
|
|
133
|
+
}
|
|
134
|
+
yield value
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// This is heavily inspired by https://stackoverflow.com/a/46432113
|
|
2
|
+
export class LRUCache<Key, Value> {
|
|
3
|
+
private readonly cache = new Map<Key, Value>()
|
|
4
|
+
|
|
5
|
+
public constructor(private readonly maxSize: number) {}
|
|
6
|
+
|
|
7
|
+
public get(key: Key): Value | undefined {
|
|
8
|
+
const value = this.cache.get(key)
|
|
9
|
+
|
|
10
|
+
if (value !== undefined) {
|
|
11
|
+
this.cache.delete(key)
|
|
12
|
+
this.cache.set(key, value)
|
|
13
|
+
}
|
|
14
|
+
return value
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
public set(key: Key, value: Value) {
|
|
18
|
+
if (this.cache.has(key)) {
|
|
19
|
+
this.cache.delete(key) // Refresh this key
|
|
20
|
+
} else if (this.cache.size === this.maxSize) {
|
|
21
|
+
this.cache.delete(this.getOldestKey()) // Evict the oldest value
|
|
22
|
+
}
|
|
23
|
+
this.cache.set(key, value)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
private getOldestKey(): Key {
|
|
27
|
+
return this.cache.keys().next().value
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export class WeakIdCache<K extends object> {
|
|
2
|
+
private readonly cache = new WeakMap<K, string>()
|
|
3
|
+
private seed = 0
|
|
4
|
+
private readonly rootId = this.seedToId(this.seed++)
|
|
5
|
+
|
|
6
|
+
public getId(key: K): string {
|
|
7
|
+
let existing = this.cache.get(key)
|
|
8
|
+
|
|
9
|
+
if (existing === undefined) {
|
|
10
|
+
this.cache.set(key, (existing = this.seedToId(this.seed++)))
|
|
11
|
+
}
|
|
12
|
+
return existing
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
public getOptionalKeyId(key: K | undefined): string {
|
|
16
|
+
if (key === undefined) {
|
|
17
|
+
return this.rootId
|
|
18
|
+
}
|
|
19
|
+
return this.getId(key)
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private seedToId(seed: number): string {
|
|
23
|
+
return seed.toFixed(0)
|
|
24
|
+
}
|
|
25
|
+
}
|