-
Notifications
You must be signed in to change notification settings - Fork 20
/
base64u.ts
90 lines (71 loc) · 2.73 KB
/
base64u.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
/**
* Base64u - URL-Safe Base64 variant no padding.
* Based on https://gist.github.com/jonleighton/958841
*/
const baseCharset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'
const lookup = new Uint8Array(256)
for (let i = 0; i < 62; i++) {
lookup[baseCharset.charCodeAt(i)] = i
}
// support both urlsafe and standard base64
lookup[43] = lookup[45] = 62
lookup[47] = lookup[95] = 63
export function encode(data: Uint8Array, urlSafe = true): string {
const byteLength = data.byteLength
const byteRemainder = byteLength % 3
const mainLength = byteLength - byteRemainder
const charset = baseCharset + (urlSafe ? '-_' : '+/')
const parts: string[] = []
let a: number
let b: number
let c: number
let d: number
let chunk: number
// Main loop deals with bytes in chunks of 3
for (let i = 0; i < mainLength; i += 3) {
// Combine the three bytes into a single integer
chunk = (data[i] << 16) | (data[i + 1] << 8) | data[i + 2]
// Use bitmasks to extract 6-bit segments from the triplet
a = (chunk & 16515072) >> 18 // 16515072 = (2^6 - 1) << 18
b = (chunk & 258048) >> 12 // 258048 = (2^6 - 1) << 12
c = (chunk & 4032) >> 6 // 4032 = (2^6 - 1) << 6
d = chunk & 63 // 63 = 2^6 - 1
// Convert the raw binary segments to the appropriate ASCII encoding
parts.push(charset[a] + charset[b] + charset[c] + charset[d])
}
// Deal with the remaining bytes
if (byteRemainder === 1) {
chunk = data[mainLength]
a = (chunk & 252) >> 2 // 252 = (2^6 - 1) << 2
// Set the 4 least significant bits to zero
b = (chunk & 3) << 4 // 3 = 2^2 - 1
parts.push(charset[a] + charset[b])
} else if (byteRemainder === 2) {
chunk = (data[mainLength] << 8) | data[mainLength + 1]
a = (chunk & 64512) >> 10 // 64512 = (2^6 - 1) << 10
b = (chunk & 1008) >> 4 // 1008 = (2^6 - 1) << 4
// Set the 2 least significant bits to zero
c = (chunk & 15) << 2 // 15 = 2^4 - 1
parts.push(charset[a] + charset[b] + charset[c])
}
return parts.join('')
}
export function decode(input: string): Uint8Array {
const byteLength = input.length * 0.75
const data = new Uint8Array(byteLength)
let a: number
let b: number
let c: number
let d: number
let p = 0
for (let i = 0; i < input.length; i += 4) {
a = lookup[input.charCodeAt(i)]
b = lookup[input.charCodeAt(i + 1)]
c = lookup[input.charCodeAt(i + 2)]
d = lookup[input.charCodeAt(i + 3)]
data[p++] = (a << 2) | (b >> 4)
data[p++] = ((b & 15) << 4) | (c >> 2)
data[p++] = ((c & 3) << 6) | (d & 63)
}
return data
}