flatpack-json 9.6.4 → 9.8.0
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/CompactStorage.d.mts +10 -0
- package/dist/CompactStorage.mjs +7 -0
- package/dist/Flatpack.d.mts +2 -86
- package/dist/Flatpack.mjs +5 -610
- package/dist/FlatpackData.d.mts +24 -0
- package/dist/FlatpackData.mjs +122 -0
- package/dist/FlatpackV1.d.mts +100 -0
- package/dist/FlatpackV1.mjs +642 -0
- package/dist/FlatpackV2.d.mts +16 -0
- package/dist/FlatpackV2.mjs +41 -0
- package/dist/RefCounter.d.mts +17 -0
- package/dist/RefCounter.mjs +42 -0
- package/dist/RefElements.d.mts +2 -2
- package/dist/Trie.d.mts +37 -7
- package/dist/Trie.mjs +95 -4
- package/dist/WeakCache.d.mts +15 -0
- package/dist/WeakCache.mjs +41 -0
- package/dist/flatpackUtil.d.mts +1 -2
- package/dist/flatpacked.d.mts +14 -0
- package/dist/flatpacked.mjs +129 -0
- package/dist/optimizeFlatpacked.mjs +54 -71
- package/dist/proxy.mjs +0 -1
- package/dist/storage.d.mts +3 -64
- package/dist/storage.mjs +31 -439
- package/dist/storageV1.d.mts +67 -0
- package/dist/storageV1.mjs +445 -0
- package/dist/storageV2.d.mts +70 -0
- package/dist/storageV2.mjs +451 -0
- package/dist/stringTable.d.mts +39 -0
- package/dist/stringTable.mjs +213 -0
- package/dist/stringify.d.mts +6 -1
- package/dist/stringify.mjs +31 -2
- package/dist/types.d.mts +63 -15
- package/dist/types.mjs +6 -2
- package/dist/unpack.d.mts +1 -1
- package/dist/unpack.mjs +70 -31
- package/dist/unpackedAnnotation.d.mts +23 -0
- package/dist/unpackedAnnotation.mjs +32 -0
- package/package.json +5 -5
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare class RefCounter<T> {
|
|
2
|
+
#private;
|
|
3
|
+
constructor(values?: Iterable<T>);
|
|
4
|
+
/**
|
|
5
|
+
* Increment the reference count for a value. If the value does not exist in the map, it will be added with a count of 1.
|
|
6
|
+
*/
|
|
7
|
+
add(value: T): void;
|
|
8
|
+
set(value: T, count: number): void;
|
|
9
|
+
has(value: T): boolean;
|
|
10
|
+
get(value: T): number;
|
|
11
|
+
isReferenced(value: T): boolean;
|
|
12
|
+
clear(): void;
|
|
13
|
+
delete(value: T): boolean;
|
|
14
|
+
[Symbol.iterator](): IterableIterator<[T, number]>;
|
|
15
|
+
toJSON(): Map<T, number>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=RefCounter.d.mts.map
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export class RefCounter {
|
|
2
|
+
#refCounts = new Map();
|
|
3
|
+
constructor(values) {
|
|
4
|
+
if (values) {
|
|
5
|
+
for (const value of values) {
|
|
6
|
+
this.add(value);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Increment the reference count for a value. If the value does not exist in the map, it will be added with a count of 1.
|
|
12
|
+
*/
|
|
13
|
+
add(value) {
|
|
14
|
+
const count = this.#refCounts.get(value) ?? 0;
|
|
15
|
+
this.#refCounts.set(value, count + 1);
|
|
16
|
+
}
|
|
17
|
+
set(value, count) {
|
|
18
|
+
this.#refCounts.set(value, count);
|
|
19
|
+
}
|
|
20
|
+
has(value) {
|
|
21
|
+
return this.#refCounts.has(value);
|
|
22
|
+
}
|
|
23
|
+
get(value) {
|
|
24
|
+
return this.#refCounts.get(value) ?? 0;
|
|
25
|
+
}
|
|
26
|
+
isReferenced(value) {
|
|
27
|
+
return !!this.get(value);
|
|
28
|
+
}
|
|
29
|
+
clear() {
|
|
30
|
+
this.#refCounts.clear();
|
|
31
|
+
}
|
|
32
|
+
delete(value) {
|
|
33
|
+
return this.#refCounts.delete(value);
|
|
34
|
+
}
|
|
35
|
+
[Symbol.iterator]() {
|
|
36
|
+
return this.#refCounts.entries();
|
|
37
|
+
}
|
|
38
|
+
toJSON() {
|
|
39
|
+
return this.#refCounts;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
//# sourceMappingURL=RefCounter.mjs.map
|
package/dist/RefElements.d.mts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { ArrayElement, BigIntElement, DateElement,
|
|
1
|
+
import type { ArrayElement, BigIntElement, DateElement, FlatpackIndex, MapElement, ObjectElement, ObjectWrapperElement, RegExpElement, SetElement, SimplePrimitive, StringElement, SubStringElement } from './types.mjs';
|
|
2
2
|
export interface BaseRef {
|
|
3
3
|
/** Unique id of the reference */
|
|
4
4
|
id: number;
|
|
5
5
|
}
|
|
6
|
-
export type FnIndexLookup = (ref: RefElements | undefined) =>
|
|
6
|
+
export type FnIndexLookup = (ref: RefElements | undefined) => FlatpackIndex;
|
|
7
7
|
export interface RefElement<T> extends BaseRef {
|
|
8
8
|
toElement(lookupFn: FnIndexLookup): T;
|
|
9
9
|
clone(): RefElement<T>;
|
package/dist/Trie.d.mts
CHANGED
|
@@ -1,18 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated This class is a bit wonky and will be removed in the future. Use {@link Trie} instead.
|
|
3
|
+
*/
|
|
4
|
+
export declare class TrieOfStrings<T> {
|
|
5
|
+
root: RootNode<string, T>;
|
|
6
|
+
/**
|
|
7
|
+
* Add a string to the Trie.
|
|
8
|
+
* The behavior is a bit wonky and will get deprecated in the future.
|
|
9
|
+
* Wonky behavior:
|
|
10
|
+
* - It adds the data element to every new node created.
|
|
11
|
+
*/
|
|
12
|
+
add(key: string | Iterable<string>, data: T): TrieNode<string, T>;
|
|
4
13
|
find(key: string | Iterable<string>): {
|
|
5
14
|
data: T | undefined;
|
|
6
15
|
found: string;
|
|
7
16
|
} | undefined;
|
|
17
|
+
findNode(key: string | Iterable<string>): FoundNode<string, T> | undefined;
|
|
8
18
|
}
|
|
9
|
-
|
|
10
|
-
|
|
19
|
+
export declare class Trie<K, T> {
|
|
20
|
+
root: TrieNode<K, T>;
|
|
21
|
+
size: number;
|
|
22
|
+
set(key: Iterable<K>, data: T): TrieNode<K, T>;
|
|
23
|
+
get(key: Iterable<K>): T | undefined;
|
|
24
|
+
has(key: Iterable<K>): boolean;
|
|
25
|
+
delete(key: Iterable<K>): boolean;
|
|
26
|
+
clear(): void;
|
|
27
|
+
/**
|
|
28
|
+
* Look for the longest prefix of the key that exists in the trie and return the node and the found prefix.
|
|
29
|
+
* @param key - The key to search for.
|
|
30
|
+
* @returns The node and path found. If the full key is not found, `found` will be false.
|
|
31
|
+
*/
|
|
32
|
+
findNode(key: Iterable<K>): FoundNode<K, T> | undefined;
|
|
33
|
+
}
|
|
34
|
+
type ChildMap<K, T> = Map<K, TrieNode<K, T>>;
|
|
35
|
+
interface TrieNode<K, T> {
|
|
11
36
|
d?: T | undefined;
|
|
12
|
-
c?: ChildMap<T>;
|
|
37
|
+
c?: ChildMap<K, T>;
|
|
13
38
|
}
|
|
14
|
-
interface RootNode<T> extends TrieNode<T> {
|
|
39
|
+
interface RootNode<K, T> extends TrieNode<K, T> {
|
|
15
40
|
d: undefined;
|
|
16
41
|
}
|
|
42
|
+
interface FoundNode<K, T> {
|
|
43
|
+
node: TrieNode<K, T>;
|
|
44
|
+
found: boolean;
|
|
45
|
+
path: K[];
|
|
46
|
+
}
|
|
17
47
|
export {};
|
|
18
48
|
//# sourceMappingURL=Trie.d.mts.map
|
package/dist/Trie.mjs
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* @deprecated This class is a bit wonky and will be removed in the future. Use {@link Trie} instead.
|
|
3
|
+
*/
|
|
4
|
+
export class TrieOfStrings {
|
|
2
5
|
root = { d: undefined, c: new Map() };
|
|
6
|
+
/**
|
|
7
|
+
* Add a string to the Trie.
|
|
8
|
+
* The behavior is a bit wonky and will get deprecated in the future.
|
|
9
|
+
* Wonky behavior:
|
|
10
|
+
* - It adds the data element to every new node created.
|
|
11
|
+
*/
|
|
3
12
|
add(key, data) {
|
|
4
13
|
let node = this.root;
|
|
5
14
|
for (const k of key) {
|
|
@@ -13,23 +22,105 @@ export class Trie {
|
|
|
13
22
|
}
|
|
14
23
|
node = n;
|
|
15
24
|
}
|
|
25
|
+
return node;
|
|
16
26
|
}
|
|
17
27
|
find(key) {
|
|
28
|
+
const foundNode = this.findNode(key);
|
|
29
|
+
if (!foundNode) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const { node, path: found } = foundNode;
|
|
33
|
+
return { data: node.d, found: found.join('') };
|
|
34
|
+
}
|
|
35
|
+
findNode(key) {
|
|
36
|
+
let node = this.root;
|
|
37
|
+
const path = [];
|
|
38
|
+
let found = true;
|
|
39
|
+
for (const k of key) {
|
|
40
|
+
const c = node.c;
|
|
41
|
+
if (!c) {
|
|
42
|
+
found = false;
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
const n = c.get(k);
|
|
46
|
+
if (!n) {
|
|
47
|
+
found = false;
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
path.push(k);
|
|
51
|
+
node = n;
|
|
52
|
+
}
|
|
53
|
+
return { node, path, found };
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
export class Trie {
|
|
57
|
+
root = {};
|
|
58
|
+
size = 0;
|
|
59
|
+
set(key, data) {
|
|
60
|
+
let node = this.root;
|
|
61
|
+
for (const k of key) {
|
|
62
|
+
let c = node.c;
|
|
63
|
+
if (!c) {
|
|
64
|
+
node.c = c = new Map();
|
|
65
|
+
}
|
|
66
|
+
let n = c.get(k);
|
|
67
|
+
if (!n) {
|
|
68
|
+
c.set(k, (n = {}));
|
|
69
|
+
}
|
|
70
|
+
node = n;
|
|
71
|
+
}
|
|
72
|
+
const wasSet = Object.hasOwn(node, 'd');
|
|
73
|
+
node.d = data;
|
|
74
|
+
if (!wasSet) {
|
|
75
|
+
++this.size;
|
|
76
|
+
}
|
|
77
|
+
return node;
|
|
78
|
+
}
|
|
79
|
+
get(key) {
|
|
80
|
+
const f = this.findNode(key);
|
|
81
|
+
return f?.found ? f.node.d : undefined;
|
|
82
|
+
}
|
|
83
|
+
has(key) {
|
|
84
|
+
const f = this.findNode(key);
|
|
85
|
+
return !!f?.found && Object.hasOwn(f.node, 'd');
|
|
86
|
+
}
|
|
87
|
+
delete(key) {
|
|
88
|
+
const f = this.findNode(key);
|
|
89
|
+
if (!f?.found || !Object.hasOwn(f.node, 'd')) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
delete f.node.d;
|
|
93
|
+
--this.size;
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
clear() {
|
|
97
|
+
this.root = {};
|
|
98
|
+
this.size = 0;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Look for the longest prefix of the key that exists in the trie and return the node and the found prefix.
|
|
102
|
+
* @param key - The key to search for.
|
|
103
|
+
* @returns The node and path found. If the full key is not found, `found` will be false.
|
|
104
|
+
*/
|
|
105
|
+
findNode(key) {
|
|
18
106
|
let node = this.root;
|
|
19
|
-
|
|
107
|
+
const path = [];
|
|
108
|
+
let found = true;
|
|
20
109
|
for (const k of key) {
|
|
21
110
|
const c = node.c;
|
|
22
111
|
if (!c) {
|
|
112
|
+
found = false;
|
|
23
113
|
break;
|
|
24
114
|
}
|
|
25
115
|
const n = c.get(k);
|
|
26
116
|
if (!n) {
|
|
117
|
+
found = false;
|
|
27
118
|
break;
|
|
28
119
|
}
|
|
29
|
-
|
|
120
|
+
path.push(k);
|
|
30
121
|
node = n;
|
|
31
122
|
}
|
|
32
|
-
return {
|
|
123
|
+
return { node, path, found };
|
|
33
124
|
}
|
|
34
125
|
}
|
|
35
126
|
//# sourceMappingURL=Trie.mjs.map
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cache that can store values for both primitive and object keys. Primitive keys are stored in a Map,
|
|
3
|
+
* while object keys are stored in a WeakMap. This allows the cache to automatically clean up entries for
|
|
4
|
+
* objects that are no longer referenced elsewhere in the program, while still allowing primitive keys to
|
|
5
|
+
* be stored without issue.
|
|
6
|
+
*/
|
|
7
|
+
export declare class WeakCache<V> {
|
|
8
|
+
#private;
|
|
9
|
+
constructor(entries?: Iterable<readonly [unknown, V]>);
|
|
10
|
+
get(key: unknown): V | undefined;
|
|
11
|
+
set(key: unknown, value: V): void;
|
|
12
|
+
clear(): void;
|
|
13
|
+
has(key: unknown): boolean;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=WeakCache.d.mts.map
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A cache that can store values for both primitive and object keys. Primitive keys are stored in a Map,
|
|
3
|
+
* while object keys are stored in a WeakMap. This allows the cache to automatically clean up entries for
|
|
4
|
+
* objects that are no longer referenced elsewhere in the program, while still allowing primitive keys to
|
|
5
|
+
* be stored without issue.
|
|
6
|
+
*/
|
|
7
|
+
export class WeakCache {
|
|
8
|
+
#primitiveCache = new Map();
|
|
9
|
+
#objectCache = new WeakMap();
|
|
10
|
+
constructor(entries) {
|
|
11
|
+
if (entries) {
|
|
12
|
+
for (const [key, value] of entries) {
|
|
13
|
+
this.set(key, value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
get(key) {
|
|
18
|
+
if (typeof key === 'object' && key !== null) {
|
|
19
|
+
return this.#objectCache.get(key);
|
|
20
|
+
}
|
|
21
|
+
return this.#primitiveCache.get(key);
|
|
22
|
+
}
|
|
23
|
+
set(key, value) {
|
|
24
|
+
if (typeof key === 'object' && key !== null) {
|
|
25
|
+
this.#objectCache.set(key, value);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
this.#primitiveCache.set(key, value);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
clear() {
|
|
32
|
+
this.#primitiveCache.clear();
|
|
33
|
+
this.#objectCache = new WeakMap();
|
|
34
|
+
}
|
|
35
|
+
has(key) {
|
|
36
|
+
// Note: WeakMap.has will always return false for primitive keys,
|
|
37
|
+
// so we can check both caches without first checking if it is an object.
|
|
38
|
+
return this.#primitiveCache.has(key) || this.#objectCache.has(key);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=WeakCache.mjs.map
|
package/dist/flatpackUtil.d.mts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { RefElements } from './RefElements.mjs';
|
|
2
|
-
import type { Flatpacked } from './types.mjs';
|
|
3
|
-
import { type FlattenedElement } from './types.mjs';
|
|
2
|
+
import type { Flatpacked, FlattenedElement } from './types.mjs';
|
|
4
3
|
export declare function fromElement(elem: FlattenedElement, resolve: (index: number) => RefElements): RefElements;
|
|
5
4
|
export declare function fromFlatpacked(flat: Flatpacked): RefElements[];
|
|
6
5
|
export declare class FlatpackedWrapper {
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Flatpacked, FlatpackIndex, FlattenedElement, ObjectElement, StringTableElement, UnpackMetaData } from './types.mjs';
|
|
2
|
+
export declare function getIndexesReferencedByElement(elem: FlattenedElement): FlatpackIndex[];
|
|
3
|
+
export declare function extractObjectKeyAndValueIndexes(elem: FlattenedElement): FlatpackIndex[];
|
|
4
|
+
type KeyValueIndexes = [key: FlatpackIndex, value: FlatpackIndex];
|
|
5
|
+
export declare function extractObjectKeyAndValueIndexesFrom(elem: ObjectElement): KeyValueIndexes;
|
|
6
|
+
export declare function extractObjectKeyAndValueIndexesFrom(elem: undefined): undefined;
|
|
7
|
+
export declare function extractObjectKeyAndValueIndexesFrom(elem: FlattenedElement | undefined): KeyValueIndexes | undefined;
|
|
8
|
+
export declare function getFlatpackedRootIdx(flatpack: Flatpacked): FlatpackIndex;
|
|
9
|
+
export declare function getFlatpackedRoot(flatpack: Flatpacked): FlattenedElement;
|
|
10
|
+
export declare function generateUnpackMetaData(flatpack: Flatpacked): UnpackMetaData;
|
|
11
|
+
export declare function isStringTableElement(elem: FlattenedElement | undefined): elem is StringTableElement;
|
|
12
|
+
export declare function isObjectElement(elem: FlattenedElement | undefined): elem is ObjectElement;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=flatpacked.d.mts.map
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import assert from 'node:assert';
|
|
2
|
+
import { RefCounter } from './RefCounter.mjs';
|
|
3
|
+
import { ElementType } from './types.mjs';
|
|
4
|
+
export function getIndexesReferencedByElement(elem) {
|
|
5
|
+
if (!elem) {
|
|
6
|
+
return [];
|
|
7
|
+
}
|
|
8
|
+
if (Array.isArray(elem)) {
|
|
9
|
+
return extractElementIndexes(elem);
|
|
10
|
+
}
|
|
11
|
+
if (typeof elem === 'string') {
|
|
12
|
+
return [];
|
|
13
|
+
}
|
|
14
|
+
if (typeof elem === 'number') {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
if (typeof elem === 'object') {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
assert(typeof elem === 'boolean', `Expected boolean, got ${typeof elem}`);
|
|
21
|
+
return [];
|
|
22
|
+
}
|
|
23
|
+
function extractElementIndexes(element) {
|
|
24
|
+
switch (element[0]) {
|
|
25
|
+
case ElementType.Array: {
|
|
26
|
+
return element.slice(1);
|
|
27
|
+
}
|
|
28
|
+
case ElementType.Object: {
|
|
29
|
+
return element.slice(1).filter((v) => !!v);
|
|
30
|
+
}
|
|
31
|
+
case ElementType.String: {
|
|
32
|
+
return element.slice(1);
|
|
33
|
+
}
|
|
34
|
+
case ElementType.SubString: {
|
|
35
|
+
return [element[1]];
|
|
36
|
+
}
|
|
37
|
+
case ElementType.Set: {
|
|
38
|
+
return element.slice(1, 2);
|
|
39
|
+
}
|
|
40
|
+
case ElementType.Map: {
|
|
41
|
+
return element.slice(1, 3);
|
|
42
|
+
}
|
|
43
|
+
case ElementType.RegExp: {
|
|
44
|
+
return element.slice(1, 3);
|
|
45
|
+
}
|
|
46
|
+
case ElementType.Date: {
|
|
47
|
+
return [];
|
|
48
|
+
}
|
|
49
|
+
case ElementType.BigInt: {
|
|
50
|
+
return [element[1]];
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
if (!element.length)
|
|
54
|
+
return [];
|
|
55
|
+
assert(false, 'Invalid element type.');
|
|
56
|
+
}
|
|
57
|
+
export function extractObjectKeyAndValueIndexes(elem) {
|
|
58
|
+
if (!elem || typeof elem !== 'object' || !Array.isArray(elem))
|
|
59
|
+
return [];
|
|
60
|
+
const element = elem;
|
|
61
|
+
switch (element[0]) {
|
|
62
|
+
case ElementType.Object: {
|
|
63
|
+
return element.slice(1).filter((v) => !!v);
|
|
64
|
+
}
|
|
65
|
+
case ElementType.Set: {
|
|
66
|
+
return []; // element.slice(1, 2);
|
|
67
|
+
}
|
|
68
|
+
case ElementType.Map: {
|
|
69
|
+
return []; // element.slice(1, 3);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return [];
|
|
73
|
+
}
|
|
74
|
+
export function extractObjectKeyAndValueIndexesFrom(elem) {
|
|
75
|
+
if (!isObjectElement(elem))
|
|
76
|
+
return undefined;
|
|
77
|
+
return [elem[1], elem[2]];
|
|
78
|
+
}
|
|
79
|
+
export function getFlatpackedRootIdx(flatpack) {
|
|
80
|
+
let idx = 1;
|
|
81
|
+
if (isStringTableElement(flatpack[idx])) {
|
|
82
|
+
idx++;
|
|
83
|
+
}
|
|
84
|
+
assert(!isStringTableElement(flatpack[idx]), 'String table element should be at index 1 if it exists');
|
|
85
|
+
return idx;
|
|
86
|
+
}
|
|
87
|
+
export function getFlatpackedRoot(flatpack) {
|
|
88
|
+
return flatpack[getFlatpackedRootIdx(flatpack)];
|
|
89
|
+
}
|
|
90
|
+
export function generateUnpackMetaData(flatpack) {
|
|
91
|
+
const referenced = new RefCounter();
|
|
92
|
+
const rootIndex = getFlatpackedRootIdx(flatpack);
|
|
93
|
+
calcReferenced(rootIndex);
|
|
94
|
+
// Make sure the string table is always included.
|
|
95
|
+
if (rootIndex > 1) {
|
|
96
|
+
referenced.add(1);
|
|
97
|
+
}
|
|
98
|
+
const metaData = {
|
|
99
|
+
flatpack,
|
|
100
|
+
referenced,
|
|
101
|
+
rootIndex,
|
|
102
|
+
};
|
|
103
|
+
return metaData;
|
|
104
|
+
function calcReferenced(idx) {
|
|
105
|
+
if (!idx)
|
|
106
|
+
return;
|
|
107
|
+
if (referenced.isReferenced(idx)) {
|
|
108
|
+
referenced.add(idx);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
referenced.add(idx);
|
|
112
|
+
for (const ref of getIndexesReferencedByElement(flatpack[idx])) {
|
|
113
|
+
calcReferenced(ref);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
export function isStringTableElement(elem) {
|
|
118
|
+
if (!Array.isArray(elem)) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return elem[0] === ElementType.StringTable;
|
|
122
|
+
}
|
|
123
|
+
export function isObjectElement(elem) {
|
|
124
|
+
if (!Array.isArray(elem)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
return elem[0] === ElementType.Object;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=flatpacked.mjs.map
|
|
@@ -1,43 +1,75 @@
|
|
|
1
1
|
import assert from 'node:assert';
|
|
2
|
+
import { getIndexesReferencedByElement } from './flatpacked.mjs';
|
|
3
|
+
import { StringTableBuilder } from './stringTable.mjs';
|
|
2
4
|
import { ElementType, supportedHeaders } from './types.mjs';
|
|
3
5
|
export function optimizeFlatpacked(data) {
|
|
4
|
-
const [header] = data;
|
|
6
|
+
const [header, maybeStringTable] = data;
|
|
7
|
+
if (data[1] === undefined || data[2] === undefined) {
|
|
8
|
+
return data;
|
|
9
|
+
}
|
|
5
10
|
if (!supportedHeaders.has(header)) {
|
|
6
11
|
throw new Error('Invalid header');
|
|
7
12
|
}
|
|
8
|
-
const
|
|
9
|
-
|
|
13
|
+
const stringTable = maybeStringTable && Array.isArray(maybeStringTable) && maybeStringTable[0] === ElementType.StringTable
|
|
14
|
+
? maybeStringTable
|
|
15
|
+
: undefined;
|
|
16
|
+
const startIndex = stringTable ? 2 : 1;
|
|
17
|
+
const elements = data.slice(startIndex);
|
|
18
|
+
const elementRefs = elements.map((element, index) => ({
|
|
19
|
+
origIndex: index + startIndex,
|
|
20
|
+
refCount: 0,
|
|
21
|
+
index: 0,
|
|
22
|
+
element,
|
|
23
|
+
}));
|
|
24
|
+
const indexToRefElement = new Map(elementRefs.map((refElement) => [refElement.origIndex, refElement]));
|
|
25
|
+
const stringTableBuilder = new StringTableBuilder(stringTable);
|
|
10
26
|
for (const refElement of elementRefs) {
|
|
11
|
-
|
|
12
|
-
continue;
|
|
13
|
-
}
|
|
14
|
-
const indexes = getRefIndexes(refElement.element);
|
|
27
|
+
const indexes = getIndexesReferencedByElement(refElement.element);
|
|
15
28
|
for (const index of indexes) {
|
|
29
|
+
if (index < 0) {
|
|
30
|
+
stringTableBuilder.addRef(-index);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (!index) {
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
16
36
|
const ref = indexToRefElement.get(index);
|
|
17
37
|
assert(ref, `Invalid reference index: ${index}`);
|
|
18
38
|
ref.refCount++;
|
|
19
39
|
}
|
|
20
40
|
}
|
|
21
|
-
const sortedRefElements = elementRefs.
|
|
22
|
-
sortedRefElements.forEach((refElement, index) => {
|
|
23
|
-
refElement.index = index + 2;
|
|
24
|
-
});
|
|
41
|
+
const sortedRefElements = elementRefs.sort((a, b) => b.refCount - a.refCount || a.origIndex - b.origIndex);
|
|
25
42
|
const indexMap = new Map([
|
|
26
43
|
[0, 0],
|
|
27
44
|
[1, 1],
|
|
28
45
|
]);
|
|
29
|
-
|
|
30
|
-
indexMap.set(
|
|
31
|
-
}
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
46
|
+
if (stringTable) {
|
|
47
|
+
indexMap.set(2, 2);
|
|
48
|
+
}
|
|
49
|
+
for (const refElement of sortedRefElements) {
|
|
50
|
+
const idx = indexMap.get(refElement.origIndex) ?? indexMap.size;
|
|
51
|
+
refElement.index = idx;
|
|
52
|
+
indexMap.set(refElement.origIndex, idx);
|
|
53
|
+
}
|
|
54
|
+
for (const [oldStrIndex, newStrIndex] of stringTableBuilder.sortEntriesByRefCount()) {
|
|
55
|
+
if (!oldStrIndex || !newStrIndex) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
indexMap.set(-oldStrIndex, -newStrIndex);
|
|
59
|
+
}
|
|
60
|
+
const stringTableElements = stringTable ? [stringTableBuilder.build()] : [];
|
|
61
|
+
const result = [header, ...stringTableElements];
|
|
62
|
+
for (const refElement of sortedRefElements) {
|
|
63
|
+
const element = patchIndexes(refElement.element, indexMap);
|
|
64
|
+
result[refElement.index] = element;
|
|
65
|
+
}
|
|
66
|
+
return result;
|
|
37
67
|
}
|
|
38
68
|
function patchIndexes(elem, indexMap) {
|
|
39
69
|
function mapIndex(index) {
|
|
40
70
|
const v = indexMap.get(index);
|
|
71
|
+
if (v === undefined && index < 0)
|
|
72
|
+
return index;
|
|
41
73
|
assert(v !== undefined, `Invalid index: ${index}`);
|
|
42
74
|
return v;
|
|
43
75
|
}
|
|
@@ -76,6 +108,8 @@ function patchIndexes(elem, indexMap) {
|
|
|
76
108
|
return [element[0], mapIndex(element[1])];
|
|
77
109
|
}
|
|
78
110
|
}
|
|
111
|
+
if (!element.length)
|
|
112
|
+
return [];
|
|
79
113
|
assert(false, 'Invalid element type');
|
|
80
114
|
}
|
|
81
115
|
if (Array.isArray(elem)) {
|
|
@@ -90,58 +124,7 @@ function patchIndexes(elem, indexMap) {
|
|
|
90
124
|
if (typeof elem === 'object') {
|
|
91
125
|
return elem;
|
|
92
126
|
}
|
|
93
|
-
assert(typeof elem === 'boolean');
|
|
127
|
+
assert(typeof elem === 'boolean', `Expected boolean, got ${typeof elem}`);
|
|
94
128
|
return elem;
|
|
95
129
|
}
|
|
96
|
-
function getRefIndexes(elem) {
|
|
97
|
-
function handleArrayElement(element) {
|
|
98
|
-
switch (element[0]) {
|
|
99
|
-
case ElementType.Array: {
|
|
100
|
-
return element.slice(1);
|
|
101
|
-
}
|
|
102
|
-
case ElementType.Object: {
|
|
103
|
-
return element.slice(1).filter((v) => !!v);
|
|
104
|
-
}
|
|
105
|
-
case ElementType.String: {
|
|
106
|
-
return element.slice(1);
|
|
107
|
-
}
|
|
108
|
-
case ElementType.SubString: {
|
|
109
|
-
return [element[1]];
|
|
110
|
-
}
|
|
111
|
-
case ElementType.Set: {
|
|
112
|
-
return element.slice(1);
|
|
113
|
-
}
|
|
114
|
-
case ElementType.Map: {
|
|
115
|
-
return element.slice(1);
|
|
116
|
-
}
|
|
117
|
-
case ElementType.RegExp: {
|
|
118
|
-
return element.slice(1);
|
|
119
|
-
}
|
|
120
|
-
case ElementType.Date: {
|
|
121
|
-
return [];
|
|
122
|
-
}
|
|
123
|
-
case ElementType.BigInt: {
|
|
124
|
-
return [element[1]];
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
assert(false, 'Invalid element type');
|
|
128
|
-
}
|
|
129
|
-
if (Array.isArray(elem)) {
|
|
130
|
-
return handleArrayElement(elem);
|
|
131
|
-
}
|
|
132
|
-
if (typeof elem === 'string') {
|
|
133
|
-
return [];
|
|
134
|
-
}
|
|
135
|
-
if (typeof elem === 'number') {
|
|
136
|
-
return [];
|
|
137
|
-
}
|
|
138
|
-
if (typeof elem === 'object') {
|
|
139
|
-
if (elem === null) {
|
|
140
|
-
return [];
|
|
141
|
-
}
|
|
142
|
-
return [];
|
|
143
|
-
}
|
|
144
|
-
assert(typeof elem === 'boolean');
|
|
145
|
-
return [];
|
|
146
|
-
}
|
|
147
130
|
//# sourceMappingURL=optimizeFlatpacked.mjs.map
|