@exodus/bytes 1.0.0-rc.0 → 1.0.0-rc.10

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.
@@ -0,0 +1,118 @@
1
+ const { Buffer, TextEncoder, TextDecoder } = globalThis
2
+ const haveNativeBuffer = Buffer && !Buffer.TYPED_ARRAY_SUPPORT
3
+ let isNative = (x) => x && (haveNativeBuffer || `${x}`.includes('[native code]')) // we consider Node.js TextDecoder/TextEncoder native
4
+ if (!haveNativeBuffer && isNative(() => {})) isNative = () => false // e.g. XS, we don't want false positives
5
+
6
+ export const nativeEncoder = isNative(TextEncoder) ? new TextEncoder() : null
7
+ export const nativeDecoder = isNative(TextDecoder)
8
+ ? new TextDecoder('utf-8', { ignoreBOM: true })
9
+ : null
10
+ export const nativeBuffer = haveNativeBuffer ? Buffer : null
11
+ export const isHermes = Boolean(globalThis.HermesInternal)
12
+ export const isDeno = Boolean(globalThis.Deno)
13
+ export const isLE = new Uint8Array(Uint16Array.of(258).buffer)[0] === 2
14
+
15
+ // Actually windows-1252, compatible with ascii and latin1 decoding
16
+ // Beware that on non-latin1, i.e. on windows-1252, this is broken in ~all Node.js versions released
17
+ // in 2025 due to a regression, so we call it Latin1 as it's usable only for that
18
+ let nativeDecoderLatin1impl = null
19
+ if (isNative(TextDecoder)) {
20
+ // Not all barebone engines with TextDecoder support something except utf-8, detect
21
+ try {
22
+ nativeDecoderLatin1impl = new TextDecoder('latin1', { ignoreBOM: true })
23
+ } catch {}
24
+ }
25
+
26
+ export const nativeDecoderLatin1 = nativeDecoderLatin1impl
27
+ export const canDecoders = Boolean(nativeDecoderLatin1impl)
28
+
29
+ // Block Firefox < 146 specifically from using native hex/base64, as it's very slow there
30
+ // Refs: https://bugzilla.mozilla.org/show_bug.cgi?id=1994067 (and linked issues), fixed in 146
31
+ // Before that, all versions of Firefox >= 133 are slow
32
+ // TODO: this could be removed when < 146 usage diminishes (note ESR)
33
+ // We do not worry about false-negatives here but worry about false-positives!
34
+ function shouldSkipBuiltins() {
35
+ const g = globalThis
36
+ // First, attempt to exclude as many things as we can using trivial checks, just in case, and to not hit ua
37
+ if (haveNativeBuffer || isHermes || !g.window || g.chrome || !g.navigator) return false
38
+ try {
39
+ // This was fixed specifically in Firefox 146. Other engines except Hermes (already returned) get this right
40
+ new WeakSet().add(Symbol()) // eslint-disable-line symbol-description
41
+ return false
42
+ } catch {
43
+ // In catch and not after in case if something too smart optimizes out code in try. False-negative is acceptable in that case
44
+ if (!('onmozfullscreenerror' in g)) return false // Firefox has it (might remove in the future, but we don't care)
45
+ return /firefox/i.test(g.navigator.userAgent || '') // as simple as we can
46
+ }
47
+
48
+ return false // eslint-disable-line no-unreachable
49
+ }
50
+
51
+ export const skipWeb = shouldSkipBuiltins()
52
+
53
+ function decodePartAddition(a, start, end, m) {
54
+ let o = ''
55
+ let i = start
56
+ for (const last3 = end - 3; i < last3; i += 4) {
57
+ const x0 = a[i]
58
+ const x1 = a[i + 1]
59
+ const x2 = a[i + 2]
60
+ const x3 = a[i + 3]
61
+ o += m[x0]
62
+ o += m[x1]
63
+ o += m[x2]
64
+ o += m[x3]
65
+ }
66
+
67
+ while (i < end) o += m[a[i++]]
68
+ return o
69
+ }
70
+
71
+ // Decoding with templates is faster on Hermes
72
+ function decodePartTemplates(a, start, end, m) {
73
+ let o = ''
74
+ let i = start
75
+ for (const last15 = end - 15; i < last15; i += 16) {
76
+ const x0 = a[i]
77
+ const x1 = a[i + 1]
78
+ const x2 = a[i + 2]
79
+ const x3 = a[i + 3]
80
+ const x4 = a[i + 4]
81
+ const x5 = a[i + 5]
82
+ const x6 = a[i + 6]
83
+ const x7 = a[i + 7]
84
+ const x8 = a[i + 8]
85
+ const x9 = a[i + 9]
86
+ const x10 = a[i + 10]
87
+ const x11 = a[i + 11]
88
+ const x12 = a[i + 12]
89
+ const x13 = a[i + 13]
90
+ const x14 = a[i + 14]
91
+ const x15 = a[i + 15]
92
+ o += `${m[x0]}${m[x1]}${m[x2]}${m[x3]}${m[x4]}${m[x5]}${m[x6]}${m[x7]}${m[x8]}${m[x9]}${m[x10]}${m[x11]}${m[x12]}${m[x13]}${m[x14]}${m[x15]}`
93
+ }
94
+
95
+ while (i < end) o += m[a[i++]]
96
+ return o
97
+ }
98
+
99
+ const decodePart = isHermes ? decodePartTemplates : decodePartAddition
100
+ export function decode2string(arr, start, end, m) {
101
+ if (start - end > 30_000) {
102
+ // Limit concatenation to avoid excessive GC
103
+ // Thresholds checked on Hermes for toHex
104
+ const concat = []
105
+ for (let i = start; i < end; ) {
106
+ const step = i + 500
107
+ const iNext = step > end ? end : step
108
+ concat.push(decodePart(arr, i, iNext, m))
109
+ i = iNext
110
+ }
111
+
112
+ const res = concat.join('')
113
+ concat.length = 0
114
+ return res
115
+ }
116
+
117
+ return decodePart(arr, start, end, m)
118
+ }
@@ -0,0 +1,233 @@
1
+ import { assertUint8 } from '../assert.js'
2
+ import { nativeEncoder, nativeDecoder, isHermes } from './_utils.js'
3
+ import { encodeAscii, decodeAscii } from './latin1.js'
4
+
5
+ // See https://datatracker.ietf.org/doc/html/rfc4648
6
+
7
+ const BASE32 = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'] // RFC 4648, #6
8
+ const BASE32HEX = [...'0123456789ABCDEFGHIJKLMNOPQRSTUV'] // RFC 4648, #7
9
+ const BASE32_HELPERS = {}
10
+ const BASE32HEX_HELPERS = {}
11
+
12
+ export const E_CHAR = 'Invalid character in base32 input'
13
+ export const E_PADDING = 'Invalid base32 padding'
14
+ export const E_LENGTH = 'Invalid base32 length'
15
+ export const E_LAST = 'Invalid last chunk'
16
+
17
+ const useTemplates = isHermes // Faster on Hermes and JSC, but we use it only on Hermes
18
+
19
+ // We construct output by concatenating chars, this seems to be fine enough on modern JS engines
20
+ export function toBase32(arr, isBase32Hex, padding) {
21
+ assertUint8(arr)
22
+ const fullChunks = Math.floor(arr.length / 5)
23
+ const fullChunksBytes = fullChunks * 5
24
+ let o = ''
25
+ let i = 0
26
+
27
+ const alphabet = isBase32Hex ? BASE32HEX : BASE32
28
+ const helpers = isBase32Hex ? BASE32HEX_HELPERS : BASE32_HELPERS
29
+ if (!helpers.pairs) {
30
+ helpers.pairs = []
31
+ if (nativeDecoder) {
32
+ // Lazy to save memory in case if this is not needed
33
+ helpers.codepairs = new Uint16Array(32 * 32)
34
+ const u16 = helpers.codepairs
35
+ const u8 = new Uint8Array(u16.buffer, u16.byteOffset, u16.byteLength) // write as 1-byte to ignore BE/LE difference
36
+ for (let i = 0; i < 32; i++) {
37
+ const ic = alphabet[i].charCodeAt(0)
38
+ for (let j = 0; j < 32; j++) u8[(i << 6) | (j << 1)] = u8[(j << 6) | ((i << 1) + 1)] = ic
39
+ }
40
+ } else {
41
+ const p = helpers.pairs
42
+ for (let i = 0; i < 32; i++) {
43
+ for (let j = 0; j < 32; j++) p.push(`${alphabet[i]}${alphabet[j]}`)
44
+ }
45
+ }
46
+ }
47
+
48
+ const { pairs, codepairs } = helpers
49
+
50
+ // Fast path for complete blocks
51
+ // This whole loop can be commented out, the algorithm won't change, it's just an optimization of the next loop
52
+ if (nativeDecoder) {
53
+ const oa = new Uint16Array(fullChunks * 4)
54
+ for (let j = 0; i < fullChunksBytes; i += 5) {
55
+ const a = arr[i]
56
+ const b = arr[i + 1]
57
+ const c = arr[i + 2]
58
+ const d = arr[i + 3]
59
+ const e = arr[i + 4]
60
+ const x0 = (a << 2) | (b >> 6) // 8 + 8 - 5 - 5 = 6 left
61
+ const x1 = ((b & 0x3f) << 4) | (c >> 4) // 6 + 8 - 5 - 5 = 4 left
62
+ const x2 = ((c & 0xf) << 6) | (d >> 2) // 4 + 8 - 5 - 5 = 2 left
63
+ const x3 = ((d & 0x3) << 8) | e // 2 + 8 - 5 - 5 = 0 left
64
+ oa[j] = codepairs[x0]
65
+ oa[j + 1] = codepairs[x1]
66
+ oa[j + 2] = codepairs[x2]
67
+ oa[j + 3] = codepairs[x3]
68
+ j += 4
69
+ }
70
+
71
+ o = decodeAscii(oa)
72
+ } else if (useTemplates) {
73
+ // Templates are faster only on Hermes and JSC. Browsers have TextDecoder anyway
74
+ for (; i < fullChunksBytes; i += 5) {
75
+ const a = arr[i]
76
+ const b = arr[i + 1]
77
+ const c = arr[i + 2]
78
+ const d = arr[i + 3]
79
+ const e = arr[i + 4]
80
+ const x0 = (a << 2) | (b >> 6) // 8 + 8 - 5 - 5 = 6 left
81
+ const x1 = ((b & 0x3f) << 4) | (c >> 4) // 6 + 8 - 5 - 5 = 4 left
82
+ const x2 = ((c & 0xf) << 6) | (d >> 2) // 4 + 8 - 5 - 5 = 2 left
83
+ const x3 = ((d & 0x3) << 8) | e // 2 + 8 - 5 - 5 = 0 left
84
+ o += `${pairs[x0]}${pairs[x1]}${pairs[x2]}${pairs[x3]}`
85
+ }
86
+ } else {
87
+ for (; i < fullChunksBytes; i += 5) {
88
+ const a = arr[i]
89
+ const b = arr[i + 1]
90
+ const c = arr[i + 2]
91
+ const d = arr[i + 3]
92
+ const e = arr[i + 4]
93
+ const x0 = (a << 2) | (b >> 6) // 8 + 8 - 5 - 5 = 6 left
94
+ const x1 = ((b & 0x3f) << 4) | (c >> 4) // 6 + 8 - 5 - 5 = 4 left
95
+ const x2 = ((c & 0xf) << 6) | (d >> 2) // 4 + 8 - 5 - 5 = 2 left
96
+ const x3 = ((d & 0x3) << 8) | e // 2 + 8 - 5 - 5 = 0 left
97
+ o += pairs[x0]
98
+ o += pairs[x1]
99
+ o += pairs[x2]
100
+ o += pairs[x3]
101
+ }
102
+ }
103
+
104
+ // If we have something left, process it with a full algo
105
+ let carry = 0
106
+ let shift = 3 // First byte needs to be shifted by 3 to get 5 bits
107
+ for (; i < arr.length; i++) {
108
+ const x = arr[i]
109
+ o += alphabet[carry | (x >> shift)] // shift >= 3, so this fits
110
+ if (shift >= 5) {
111
+ shift -= 5
112
+ o += alphabet[(x >> shift) & 0x1f]
113
+ }
114
+
115
+ carry = (x << (5 - shift)) & 0x1f
116
+ shift += 3 // Each byte prints 5 bits and leaves 3 bits
117
+ }
118
+
119
+ if (shift !== 3) o += alphabet[carry] // shift 3 means we have no carry left
120
+ if (padding) o += ['', '======', '====', '===', '='][arr.length - fullChunksBytes]
121
+
122
+ return o
123
+ }
124
+
125
+ // TODO: can this be optimized? This only affects non-Hermes barebone engines though
126
+ const mapSize = nativeEncoder ? 128 : 65_536 // we have to store 64 KiB map or recheck everything if we can't decode to byte array
127
+
128
+ export function fromBase32(str, isBase32Hex) {
129
+ let inputLength = str.length
130
+ while (str[inputLength - 1] === '=') inputLength--
131
+ const paddingLength = str.length - inputLength
132
+ const tailLength = inputLength % 8
133
+ const mainLength = inputLength - tailLength // multiples of 8
134
+ if (![0, 2, 4, 5, 7].includes(tailLength)) throw new SyntaxError(E_LENGTH) // fast verification
135
+ if (paddingLength > 7 || (paddingLength !== 0 && str.length % 8 !== 0)) {
136
+ throw new SyntaxError(E_PADDING)
137
+ }
138
+
139
+ const alphabet = isBase32Hex ? BASE32HEX : BASE32
140
+ const helpers = isBase32Hex ? BASE32HEX_HELPERS : BASE32_HELPERS
141
+
142
+ if (!helpers.fromMap) {
143
+ helpers.fromMap = new Int8Array(mapSize).fill(-1) // no regex input validation here, so we map all other bytes to -1 and recheck sign
144
+ alphabet.forEach((c, i) => {
145
+ helpers.fromMap[c.charCodeAt(0)] = helpers.fromMap[c.toLowerCase().charCodeAt(0)] = i
146
+ })
147
+ }
148
+
149
+ const m = helpers.fromMap
150
+
151
+ const arr = new Uint8Array(Math.floor((inputLength * 5) / 8))
152
+ let at = 0
153
+ let i = 0
154
+
155
+ if (nativeEncoder) {
156
+ const codes = encodeAscii(str, E_CHAR)
157
+ for (; i < mainLength; i += 8) {
158
+ // each 5 bits, grouped 5 * 4 = 20
159
+ const x0 = codes[i]
160
+ const x1 = codes[i + 1]
161
+ const x2 = codes[i + 2]
162
+ const x3 = codes[i + 3]
163
+ const x4 = codes[i + 4]
164
+ const x5 = codes[i + 5]
165
+ const x6 = codes[i + 6]
166
+ const x7 = codes[i + 7]
167
+ const a = (m[x0] << 15) | (m[x1] << 10) | (m[x2] << 5) | m[x3]
168
+ const b = (m[x4] << 15) | (m[x5] << 10) | (m[x6] << 5) | m[x7]
169
+ if (a < 0 || b < 0) throw new SyntaxError(E_CHAR)
170
+ arr[at] = a >> 12
171
+ arr[at + 1] = (a >> 4) & 0xff
172
+ arr[at + 2] = ((a << 4) & 0xff) | (b >> 16)
173
+ arr[at + 3] = (b >> 8) & 0xff
174
+ arr[at + 4] = b & 0xff
175
+ at += 5
176
+ }
177
+ } else {
178
+ for (; i < mainLength; i += 8) {
179
+ // each 5 bits, grouped 5 * 4 = 20
180
+ const x0 = str.charCodeAt(i)
181
+ const x1 = str.charCodeAt(i + 1)
182
+ const x2 = str.charCodeAt(i + 2)
183
+ const x3 = str.charCodeAt(i + 3)
184
+ const x4 = str.charCodeAt(i + 4)
185
+ const x5 = str.charCodeAt(i + 5)
186
+ const x6 = str.charCodeAt(i + 6)
187
+ const x7 = str.charCodeAt(i + 7)
188
+ const a = (m[x0] << 15) | (m[x1] << 10) | (m[x2] << 5) | m[x3]
189
+ const b = (m[x4] << 15) | (m[x5] << 10) | (m[x6] << 5) | m[x7]
190
+ if (a < 0 || b < 0) throw new SyntaxError(E_CHAR)
191
+ arr[at] = a >> 12
192
+ arr[at + 1] = (a >> 4) & 0xff
193
+ arr[at + 2] = ((a << 4) & 0xff) | (b >> 16)
194
+ arr[at + 3] = (b >> 8) & 0xff
195
+ arr[at + 4] = b & 0xff
196
+ at += 5
197
+ }
198
+ }
199
+
200
+ // Last block, valid tailLength: 0 2 4 5 7, checked already
201
+ // We check last chunk to be strict
202
+ if (tailLength < 2) return arr
203
+ const ab = (m[str.charCodeAt(i++)] << 5) | m[str.charCodeAt(i++)]
204
+ if (ab < 0) throw new SyntaxError(E_CHAR)
205
+ arr[at++] = ab >> 2
206
+ if (tailLength < 4) {
207
+ if (ab & 0x3) throw new SyntaxError(E_LAST)
208
+ return arr
209
+ }
210
+
211
+ const cd = (m[str.charCodeAt(i++)] << 5) | m[str.charCodeAt(i++)]
212
+ if (cd < 0) throw new SyntaxError(E_CHAR)
213
+ arr[at++] = ((ab << 6) & 0xff) | (cd >> 4)
214
+ if (tailLength < 5) {
215
+ if (cd & 0xf) throw new SyntaxError(E_LAST)
216
+ return arr
217
+ }
218
+
219
+ const e = m[str.charCodeAt(i++)]
220
+ if (e < 0) throw new SyntaxError(E_CHAR)
221
+ arr[at++] = ((cd << 4) & 0xff) | (e >> 1) // 4 + 4
222
+ if (tailLength < 7) {
223
+ if (e & 0x1) throw new SyntaxError(E_LAST)
224
+ return arr
225
+ }
226
+
227
+ const fg = (m[str.charCodeAt(i++)] << 5) | m[str.charCodeAt(i++)]
228
+ if (fg < 0) throw new SyntaxError(E_CHAR)
229
+ arr[at++] = ((e << 7) & 0xff) | (fg >> 3) // 1 + 5 + 2
230
+ // Can't be 8, so no h
231
+ if (fg & 0x7) throw new SyntaxError(E_LAST)
232
+ return arr
233
+ }
@@ -0,0 +1,192 @@
1
+ import { assertUint8 } from '../assert.js'
2
+ import { nativeEncoder, nativeDecoder } from './_utils.js'
3
+ import { encodeAscii, decodeAscii } from './latin1.js'
4
+
5
+ // See https://datatracker.ietf.org/doc/html/rfc4648
6
+
7
+ const BASE64 = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/']
8
+ const BASE64URL = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_']
9
+ const BASE64_HELPERS = {}
10
+ const BASE64URL_HELPERS = {}
11
+
12
+ export const E_CHAR = 'Invalid character in base64 input'
13
+ export const E_PADDING = 'Invalid base64 padding'
14
+ export const E_LENGTH = 'Invalid base64 length'
15
+ export const E_LAST = 'Invalid last chunk'
16
+
17
+ // We construct output by concatenating chars, this seems to be fine enough on modern JS engines
18
+ export function toBase64(arr, isURL, padding) {
19
+ assertUint8(arr)
20
+ const fullChunks = (arr.length / 3) | 0
21
+ const fullChunksBytes = fullChunks * 3
22
+ let o = ''
23
+ let i = 0
24
+
25
+ const alphabet = isURL ? BASE64URL : BASE64
26
+ const helpers = isURL ? BASE64URL_HELPERS : BASE64_HELPERS
27
+ if (!helpers.pairs) {
28
+ helpers.pairs = []
29
+ if (nativeDecoder) {
30
+ // Lazy to save memory in case if this is not needed
31
+ helpers.codepairs = new Uint16Array(64 * 64)
32
+ const u16 = helpers.codepairs
33
+ const u8 = new Uint8Array(u16.buffer, u16.byteOffset, u16.byteLength) // write as 1-byte to ignore BE/LE difference
34
+ for (let i = 0; i < 64; i++) {
35
+ const ic = alphabet[i].charCodeAt(0)
36
+ for (let j = 0; j < 64; j++) u8[(i << 7) | (j << 1)] = u8[(j << 7) | ((i << 1) + 1)] = ic
37
+ }
38
+ } else {
39
+ const p = helpers.pairs
40
+ for (let i = 0; i < 64; i++) {
41
+ for (let j = 0; j < 64; j++) p.push(`${alphabet[i]}${alphabet[j]}`)
42
+ }
43
+ }
44
+ }
45
+
46
+ const { pairs, codepairs } = helpers
47
+
48
+ // Fast path for complete blocks
49
+ // This whole loop can be commented out, the algorithm won't change, it's just an optimization of the next loop
50
+ if (nativeDecoder) {
51
+ const oa = new Uint16Array(fullChunks * 2)
52
+ let j = 0
53
+ for (const last = arr.length - 11; i < last; i += 12, j += 8) {
54
+ const x0 = arr[i]
55
+ const x1 = arr[i + 1]
56
+ const x2 = arr[i + 2]
57
+ const x3 = arr[i + 3]
58
+ const x4 = arr[i + 4]
59
+ const x5 = arr[i + 5]
60
+ const x6 = arr[i + 6]
61
+ const x7 = arr[i + 7]
62
+ const x8 = arr[i + 8]
63
+ const x9 = arr[i + 9]
64
+ const x10 = arr[i + 10]
65
+ const x11 = arr[i + 11]
66
+ oa[j] = codepairs[(x0 << 4) | (x1 >> 4)]
67
+ oa[j + 1] = codepairs[((x1 & 0x0f) << 8) | x2]
68
+ oa[j + 2] = codepairs[(x3 << 4) | (x4 >> 4)]
69
+ oa[j + 3] = codepairs[((x4 & 0x0f) << 8) | x5]
70
+ oa[j + 4] = codepairs[(x6 << 4) | (x7 >> 4)]
71
+ oa[j + 5] = codepairs[((x7 & 0x0f) << 8) | x8]
72
+ oa[j + 6] = codepairs[(x9 << 4) | (x10 >> 4)]
73
+ oa[j + 7] = codepairs[((x10 & 0x0f) << 8) | x11]
74
+ }
75
+
76
+ // i < last here is equivalent to i < fullChunksBytes
77
+ for (const last = arr.length - 2; i < last; i += 3, j += 2) {
78
+ const a = arr[i]
79
+ const b = arr[i + 1]
80
+ const c = arr[i + 2]
81
+ oa[j] = codepairs[(a << 4) | (b >> 4)]
82
+ oa[j + 1] = codepairs[((b & 0x0f) << 8) | c]
83
+ }
84
+
85
+ o = decodeAscii(oa)
86
+ } else {
87
+ // This can be optimized by ~25% with templates on Hermes, but this codepath is not called on Hermes, it uses btoa
88
+ // Check git history for templates version
89
+ for (; i < fullChunksBytes; i += 3) {
90
+ const a = arr[i]
91
+ const b = arr[i + 1]
92
+ const c = arr[i + 2]
93
+ o += pairs[(a << 4) | (b >> 4)]
94
+ o += pairs[((b & 0x0f) << 8) | c]
95
+ }
96
+ }
97
+
98
+ // If we have something left, process it with a full algo
99
+ let carry = 0
100
+ let shift = 2 // First byte needs to be shifted by 2 to get 6 bits
101
+ const length = arr.length
102
+ for (; i < length; i++) {
103
+ const x = arr[i]
104
+ o += alphabet[carry | (x >> shift)] // shift >= 2, so this fits
105
+ if (shift === 6) {
106
+ shift = 0
107
+ o += alphabet[x & 0x3f]
108
+ }
109
+
110
+ carry = (x << (6 - shift)) & 0x3f
111
+ shift += 2 // Each byte prints 6 bits and leaves 2 bits
112
+ }
113
+
114
+ if (shift !== 2) o += alphabet[carry] // shift 2 means we have no carry left
115
+ if (padding) o += ['', '==', '='][length - fullChunksBytes]
116
+
117
+ return o
118
+ }
119
+
120
+ // TODO: can this be optimized? This only affects non-Hermes barebone engines though
121
+ const mapSize = nativeEncoder ? 128 : 65_536 // we have to store 64 KiB map or recheck everything if we can't decode to byte array
122
+
123
+ export function fromBase64(str, isURL) {
124
+ let inputLength = str.length
125
+ while (str[inputLength - 1] === '=') inputLength--
126
+ const paddingLength = str.length - inputLength
127
+ const tailLength = inputLength % 4
128
+ const mainLength = inputLength - tailLength // multiples of 4
129
+ if (tailLength === 1) throw new SyntaxError(E_LENGTH)
130
+ if (paddingLength > 3 || (paddingLength !== 0 && str.length % 4 !== 0)) {
131
+ throw new SyntaxError(E_PADDING)
132
+ }
133
+
134
+ const alphabet = isURL ? BASE64URL : BASE64
135
+ const helpers = isURL ? BASE64URL_HELPERS : BASE64_HELPERS
136
+
137
+ if (!helpers.fromMap) {
138
+ helpers.fromMap = new Int8Array(mapSize).fill(-1) // no regex input validation here, so we map all other bytes to -1 and recheck sign
139
+ alphabet.forEach((c, i) => (helpers.fromMap[c.charCodeAt(0)] = i))
140
+ }
141
+
142
+ const m = helpers.fromMap
143
+
144
+ const arr = new Uint8Array(Math.floor((inputLength * 3) / 4))
145
+ let at = 0
146
+ let i = 0
147
+
148
+ if (nativeEncoder) {
149
+ const codes = encodeAscii(str, E_CHAR)
150
+ for (; i < mainLength; i += 4) {
151
+ const c0 = codes[i]
152
+ const c1 = codes[i + 1]
153
+ const c2 = codes[i + 2]
154
+ const c3 = codes[i + 3]
155
+ const a = (m[c0] << 18) | (m[c1] << 12) | (m[c2] << 6) | m[c3]
156
+ if (a < 0) throw new SyntaxError(E_CHAR)
157
+ arr[at] = a >> 16
158
+ arr[at + 1] = (a >> 8) & 0xff
159
+ arr[at + 2] = a & 0xff
160
+ at += 3
161
+ }
162
+ } else {
163
+ for (; i < mainLength; i += 4) {
164
+ const c0 = str.charCodeAt(i)
165
+ const c1 = str.charCodeAt(i + 1)
166
+ const c2 = str.charCodeAt(i + 2)
167
+ const c3 = str.charCodeAt(i + 3)
168
+ const a = (m[c0] << 18) | (m[c1] << 12) | (m[c2] << 6) | m[c3]
169
+ if (a < 0) throw new SyntaxError(E_CHAR)
170
+ arr[at] = a >> 16
171
+ arr[at + 1] = (a >> 8) & 0xff
172
+ arr[at + 2] = a & 0xff
173
+ at += 3
174
+ }
175
+ }
176
+
177
+ // Can be 0, 2 or 3, verified by padding checks already
178
+ if (tailLength < 2) return arr // 0
179
+ const ab = (m[str.charCodeAt(i++)] << 6) | m[str.charCodeAt(i++)]
180
+ if (ab < 0) throw new SyntaxError(E_CHAR)
181
+ arr[at++] = ab >> 4
182
+ if (tailLength < 3) {
183
+ if (ab & 0xf) throw new SyntaxError(E_LAST)
184
+ return arr // 2
185
+ }
186
+
187
+ const c = m[str.charCodeAt(i++)]
188
+ if (c < 0) throw new SyntaxError(E_CHAR)
189
+ arr[at++] = ((ab << 4) & 0xff) | (c >> 2)
190
+ if (c & 0x3) throw new SyntaxError(E_LAST)
191
+ return arr // 3
192
+ }