-
-
Notifications
You must be signed in to change notification settings - Fork 80.7k
Expand file tree
/
Copy pathbase64.ts
More file actions
81 lines (76 loc) · 2.18 KB
/
Copy pathbase64.ts
File metadata and controls
81 lines (76 loc) · 2.18 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
/** Estimates decoded bytes without allocating a cleaned copy of the base64 payload. */
export function estimateBase64DecodedBytes(base64: string): number {
// Avoid `trim()`/`replace()` here: they allocate a second (potentially huge) string.
// We only need a conservative decoded-size estimate to enforce budgets before Buffer.from(..., "base64").
let effectiveLen = 0;
for (let i = 0; i < base64.length; i += 1) {
const code = base64.charCodeAt(i);
// Treat ASCII control + space as whitespace; base64 decoders commonly ignore these.
if (code <= 0x20) {
continue;
}
effectiveLen += 1;
}
if (effectiveLen === 0) {
return 0;
}
let padding = 0;
// Find last non-whitespace char(s) to detect '=' padding without allocating/copying.
let end = base64.length - 1;
while (end >= 0 && base64.charCodeAt(end) <= 0x20) {
end -= 1;
}
if (end >= 0 && base64[end] === "=") {
padding = 1;
end -= 1;
while (end >= 0 && base64.charCodeAt(end) <= 0x20) {
end -= 1;
}
if (end >= 0 && base64[end] === "=") {
padding = 2;
}
}
const estimated = Math.floor((effectiveLen * 3) / 4) - padding;
return Math.max(0, estimated);
}
function isBase64DataChar(code: number): boolean {
return (
(code >= 0x41 && code <= 0x5a) ||
(code >= 0x61 && code <= 0x7a) ||
(code >= 0x30 && code <= 0x39) ||
code === 0x2b ||
code === 0x2f
);
}
/**
* Normalizes and validates a base64 string, returning canonical no-whitespace
* base64 only when the input has valid alphabet, padding, and length.
*/
export function canonicalizeBase64(base64: string): string | undefined {
let cleaned = "";
let padding = 0;
let sawPadding = false;
for (let i = 0; i < base64.length; i += 1) {
const code = base64.charCodeAt(i);
if (code <= 0x20) {
continue;
}
if (code === 0x3d) {
padding += 1;
if (padding > 2) {
return undefined;
}
sawPadding = true;
cleaned += "=";
continue;
}
if (sawPadding || !isBase64DataChar(code)) {
return undefined;
}
cleaned += base64[i];
}
if (!cleaned || cleaned.length % 4 !== 0) {
return undefined;
}
return cleaned;
}