-
Notifications
You must be signed in to change notification settings - Fork 5
/
size_of.ts
138 lines (130 loc) Β· 3.31 KB
/
size_of.ts
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
/**
* An internal module to estimate the byte size of a value.
*
* @module
*/
const encoder = new TextEncoder();
function sizeOfString(str: string) {
return encoder.encode(str).byteLength + 4;
}
function sizeOfError(seen: WeakSet<object>, error: Error) {
seen.add(error);
let bytes = error.name.length + sizeOfString(error.message);
if (error.stack) {
bytes += sizeOfString(error.stack);
}
if (error.cause) {
bytes += getCalc(seen)(error.cause);
}
return bytes - 4;
}
function sizeOfMap(seen: WeakSet<object>, map: Map<unknown, unknown>) {
seen.add(map);
let bytes = 0;
for (const [key, value] of map) {
bytes += getCalc(seen)(key) - 1;
bytes += getCalc(seen)(value) - 1;
}
return bytes - 1;
}
function sizeOfSet(seen: WeakSet<object>, set: Set<unknown>) {
seen.add(set);
let bytes = 0;
for (const value of set) {
bytes += getCalc(seen)(value) - 1;
}
return bytes;
}
function sizeOfObject(
seen: WeakSet<object>,
value: Record<string, unknown>,
) {
let bytes = 0;
for (const key of Object.keys(value)) {
if (typeof value[key] === "object" && value[key] !== null) {
if (seen.has(value)) {
continue;
}
seen.add(value[key] as object);
}
bytes += getCalc(seen)(key);
try {
bytes += getCalc(seen)(value[key]);
} catch (error) {
if (error instanceof RangeError) {
bytes = 0;
}
}
}
return Math.max(bytes + 1, 5);
}
function getCalc(seen: WeakSet<object>): (value: unknown) => number {
return function calc(value: unknown) {
switch (typeof value) {
case "string":
return sizeOfString(value);
case "boolean":
return 3;
case "number":
return value < 64
? 4
: value < 8_192
? 5
: value < 1_048_576
? 6
: value < 134_217_728
? 7
: value < 2_147_483_648
? 8
: 11;
case "bigint":
return 12;
case "undefined":
return 3;
case "object":
if (value === null) {
return 3;
}
if (ArrayBuffer.isView(value) || value instanceof ArrayBuffer) {
return value.byteLength + 9;
}
if (Array.isArray(value)) {
return value.map(getCalc(seen)).reduce(
(acc, curr) => acc + curr - 1,
0,
);
}
if (value instanceof Date) {
return 11;
}
if (value instanceof RegExp) {
return encoder.encode(value.source).byteLength + 6;
}
if (value instanceof Error) {
return sizeOfError(seen, value);
}
if (value instanceof Set) {
return sizeOfSet(seen, value);
}
if (value instanceof Map) {
return sizeOfMap(seen, value);
}
if (value instanceof Deno.KvU64) {
return 12;
}
return sizeOfObject(seen, value as Record<string | symbol, unknown>);
default:
return 0;
}
};
}
/**
* Estimates the size, in bytes, of the V8 serialized form of the value, which
* is used to determine the size of messages being stored with Deno KV.
*
* This exists when V8 serialize is not available, like when running on Deno
* Deploy.
*/
export function sizeOf(value: unknown): number {
return getCalc(new WeakSet())(value);
}