@exodus/bytes 1.14.1 → 1.15.1
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/README.md +61 -168
- package/array.d.ts +30 -3
- package/array.js +18 -0
- package/assert.js +1 -1
- package/base32.d.ts +31 -2
- package/base32.js +18 -20
- package/base58.d.ts +4 -2
- package/base58.js +4 -5
- package/base58check.d.ts +8 -4
- package/base64.d.ts +9 -3
- package/base64.js +14 -11
- package/bigint.d.ts +2 -1
- package/encoding-browser.d.ts +12 -1
- package/encoding-browser.js +1 -1
- package/encoding-browser.native.js +10 -1
- package/encoding-lite.d.ts +2 -1
- package/encoding-lite.js +2 -0
- package/encoding.d.ts +29 -0
- package/encoding.js +2 -0
- package/fallback/_utils.js +41 -1
- package/fallback/base32.js +20 -11
- package/fallback/base58check.js +5 -6
- package/fallback/encoding.js +14 -4
- package/fallback/encoding.util.js +7 -6
- package/fallback/hex.js +1 -1
- package/fallback/latin1.js +1 -0
- package/fallback/multi-byte.js +6 -6
- package/hex.d.ts +2 -1
- package/hex.js +3 -4
- package/hex.node.js +15 -6
- package/package.json +3 -3
- package/single-byte.d.ts +7 -0
- package/single-byte.js +19 -19
- package/single-byte.node.js +17 -10
- package/utf16.node.js +2 -1
- package/utf8.d.ts +4 -2
- package/utf8.js +3 -4
- package/utf8.node.js +12 -12
package/base64.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { assertEmptyRest } from './assert.js'
|
|
2
|
-
import {
|
|
3
|
-
import { assertU8, E_STRING } from './fallback/_utils.js'
|
|
2
|
+
import { assertU8, fromUint8, fromBuffer, E_STRING } from './fallback/_utils.js'
|
|
4
3
|
import { isHermes } from './fallback/platform.js'
|
|
5
4
|
import { decodeLatin1, encodeLatin1 } from './fallback/latin1.js'
|
|
6
5
|
import * as js from './fallback/base64.js'
|
|
@@ -75,6 +74,7 @@ export function fromBase64url(str, options) {
|
|
|
75
74
|
|
|
76
75
|
// By default accepts both padded and non-padded variants, base64 or base64url
|
|
77
76
|
export function fromBase64any(str, { format = 'uint8', padding = 'both', ...rest } = {}) {
|
|
77
|
+
if (typeof str !== 'string') throw new TypeError(E_STRING)
|
|
78
78
|
const isBase64url = !str.includes('+') && !str.includes('/') // likely to fail fast, as most input is non-url, also double scan is faster than regex
|
|
79
79
|
return fromBase64common(str, isBase64url, padding, format, rest)
|
|
80
80
|
}
|
|
@@ -96,7 +96,7 @@ function fromBase64common(str, isBase64url, padding, format, rest) {
|
|
|
96
96
|
throw new TypeError('Invalid padding option')
|
|
97
97
|
}
|
|
98
98
|
|
|
99
|
-
return
|
|
99
|
+
return fromBase64impl(str, isBase64url, padding, format)
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
// ASCII whitespace is U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or U+0020 SPACE
|
|
@@ -114,7 +114,7 @@ function noWhitespaceSeen(str, arr) {
|
|
|
114
114
|
let fromBase64impl
|
|
115
115
|
if (Uint8Array.fromBase64) {
|
|
116
116
|
// NOTICE: this is actually slower than our JS impl in older JavaScriptCore and (slightly) in SpiderMonkey, but faster on V8 and new JavaScriptCore
|
|
117
|
-
fromBase64impl = (str, isBase64url, padding) => {
|
|
117
|
+
fromBase64impl = (str, isBase64url, padding, format) => {
|
|
118
118
|
const alphabet = isBase64url ? 'base64url' : 'base64'
|
|
119
119
|
|
|
120
120
|
let arr
|
|
@@ -136,20 +136,22 @@ if (Uint8Array.fromBase64) {
|
|
|
136
136
|
// We don't allow whitespace in input, but that can be rechecked based on output length
|
|
137
137
|
// All other chars are checked natively
|
|
138
138
|
if (!noWhitespaceSeen(str, arr)) throw new SyntaxError(E_CHAR)
|
|
139
|
-
return arr
|
|
139
|
+
return fromUint8(arr, format)
|
|
140
140
|
}
|
|
141
141
|
} else if (haveNativeBuffer) {
|
|
142
|
-
fromBase64impl = (str, isBase64url, padding) => {
|
|
143
|
-
const
|
|
142
|
+
fromBase64impl = (str, isBase64url, padding, format) => {
|
|
143
|
+
const size = Buffer.byteLength(str, 'base64')
|
|
144
|
+
const arr = Buffer.allocUnsafeSlow(size) // non-pooled
|
|
145
|
+
if (arr.base64Write(str) !== size) throw new SyntaxError(E_PADDING)
|
|
144
146
|
// Rechecking by re-encoding is cheaper than regexes on Node.js
|
|
145
147
|
const got = isBase64url ? maybeUnpad(str, padding === false) : maybePad(str, padding !== true)
|
|
146
148
|
const valid = isBase64url ? arr.base64urlSlice(0, arr.length) : arr.base64Slice(0, arr.length)
|
|
147
149
|
if (got !== valid) throw new SyntaxError(E_PADDING)
|
|
148
|
-
return arr // fully checked
|
|
150
|
+
return fromBuffer(arr, format) // fully checked
|
|
149
151
|
}
|
|
150
152
|
} else if (shouldUseAtob) {
|
|
151
153
|
// atob is faster than manual parsing on Hermes
|
|
152
|
-
fromBase64impl = (str, isBase64url, padding) => {
|
|
154
|
+
fromBase64impl = (str, isBase64url, padding, format) => {
|
|
153
155
|
let arr
|
|
154
156
|
if (isBase64url) {
|
|
155
157
|
if (/[\t\n\f\r +/]/.test(str)) throw new SyntaxError(E_CHAR) // atob verifies other invalid input
|
|
@@ -171,8 +173,9 @@ if (Uint8Array.fromBase64) {
|
|
|
171
173
|
if (expected !== end) throw new SyntaxError(E_LAST)
|
|
172
174
|
}
|
|
173
175
|
|
|
174
|
-
return arr
|
|
176
|
+
return fromUint8(arr, format)
|
|
175
177
|
}
|
|
176
178
|
} else {
|
|
177
|
-
fromBase64impl = (str, isBase64url, padding
|
|
179
|
+
fromBase64impl = (str, isBase64url, padding, format) =>
|
|
180
|
+
fromUint8(js.fromBase64(str, isBase64url), format) // validated in js
|
|
178
181
|
}
|
package/bigint.d.ts
CHANGED
|
@@ -34,8 +34,9 @@ export interface FromBigIntOptions {
|
|
|
34
34
|
* @returns The converted bytes in big-endian format
|
|
35
35
|
*/
|
|
36
36
|
export function fromBigInt(bigint: bigint, options: { length: number; format?: 'uint8' }): Uint8ArrayBuffer;
|
|
37
|
+
export function fromBigInt(bigint: bigint, options: { length: number; format: 'arraybuffer' }): ArrayBuffer;
|
|
37
38
|
export function fromBigInt(bigint: bigint, options: { length: number; format: 'buffer' }): Buffer;
|
|
38
|
-
export function fromBigInt(bigint: bigint, options: FromBigIntOptions): Uint8ArrayBuffer | Buffer;
|
|
39
|
+
export function fromBigInt(bigint: bigint, options: FromBigIntOptions): Uint8ArrayBuffer | ArrayBuffer | Buffer;
|
|
39
40
|
|
|
40
41
|
/**
|
|
41
42
|
* Convert a Uint8Array or Buffer to a BigInt
|
package/encoding-browser.d.ts
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
* Same as `@exodus/bytes/encoding.js`, but in browsers instead of polyfilling just uses whatever the
|
|
3
3
|
* browser provides, drastically reducing the bundle size (to less than 2 KiB gzipped).
|
|
4
4
|
*
|
|
5
|
+
* Does not provide `isomorphicDecode` and `isomorphicEncode` exports.
|
|
6
|
+
*
|
|
5
7
|
* ```js
|
|
6
8
|
* import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-browser.js'
|
|
7
9
|
* import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-browser.js' // Requires Streams
|
|
@@ -21,4 +23,13 @@
|
|
|
21
23
|
* @module @exodus/bytes/encoding-browser.js
|
|
22
24
|
*/
|
|
23
25
|
|
|
24
|
-
export
|
|
26
|
+
export {
|
|
27
|
+
TextDecoder,
|
|
28
|
+
TextEncoder,
|
|
29
|
+
TextDecoderStream,
|
|
30
|
+
TextEncoderStream,
|
|
31
|
+
normalizeEncoding,
|
|
32
|
+
getBOMEncoding,
|
|
33
|
+
labelToName,
|
|
34
|
+
legacyHookDecode,
|
|
35
|
+
} from './encoding.js'
|
package/encoding-browser.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export * from './encoding.js'
|
|
1
|
+
export * from './encoding-browser.native.js'
|
package/encoding-lite.d.ts
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The exact same exports as `@exodus/bytes/encoding.js` are also exported as
|
|
3
3
|
* `@exodus/bytes/encoding-lite.js`, with the difference that the lite version does not load
|
|
4
|
-
* multi-byte `TextDecoder` encodings by default to reduce bundle size
|
|
4
|
+
* multi-byte `TextDecoder` encodings by default to reduce bundle size ~12x.
|
|
5
5
|
*
|
|
6
6
|
* ```js
|
|
7
7
|
* import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-lite.js'
|
|
8
8
|
* import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-lite.js' // Requires Streams
|
|
9
|
+
* import { isomorphicDecode, isomorphicEncode } from '@exodus/bytes/encoding-lite.js'
|
|
9
10
|
*
|
|
10
11
|
* // Hooks for standards
|
|
11
12
|
* import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding-lite.js'
|
package/encoding-lite.js
CHANGED
package/encoding.d.ts
CHANGED
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
* ```js
|
|
10
10
|
* import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding.js'
|
|
11
11
|
* import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding.js' // Requires Streams
|
|
12
|
+
* import { isomorphicDecode, isomorphicEncode } from '@exodus/bytes/encoding.js'
|
|
12
13
|
*
|
|
13
14
|
* // Hooks for standards
|
|
14
15
|
* import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding.js'
|
|
@@ -90,6 +91,34 @@ export function legacyHookDecode(
|
|
|
90
91
|
fallbackEncoding?: string
|
|
91
92
|
): string;
|
|
92
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Implements [isomorphic decode](https://infra.spec.whatwg.org/#isomorphic-decode).
|
|
96
|
+
*
|
|
97
|
+
* Given a `TypedArray` or an `ArrayBuffer` instance `input`, creates a string of the same length
|
|
98
|
+
* as input byteLength, using bytes from input as codepoints.
|
|
99
|
+
*
|
|
100
|
+
* E.g. for `Uint8Array` input, this is similar to `String.fromCodePoint(...input)`.
|
|
101
|
+
*
|
|
102
|
+
* Wider `TypedArray` inputs, e.g. `Uint16Array`, are interpreted as underlying _bytes_.
|
|
103
|
+
*
|
|
104
|
+
* @param input - The bytes to decode
|
|
105
|
+
* @returns The decoded string
|
|
106
|
+
*/
|
|
107
|
+
export function isomorphicDecode(input: ArrayBufferLike | ArrayBufferView): string;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Implements [isomorphic encode](https://infra.spec.whatwg.org/#isomorphic-encode).
|
|
111
|
+
*
|
|
112
|
+
* Given a string, creates an `Uint8Array` of the same length with the string codepoints as byte values.
|
|
113
|
+
*
|
|
114
|
+
* Accepts only [isomorphic string](https://infra.spec.whatwg.org/#isomorphic-string) input
|
|
115
|
+
* and asserts that, throwing on any strings containing codepoints higher than `U+00FF`.
|
|
116
|
+
*
|
|
117
|
+
* @param input - The bytes to decode
|
|
118
|
+
* @returns An Uint8Array containing the input bytes.
|
|
119
|
+
*/
|
|
120
|
+
export function isomorphicEncode(str: string): Uint8Array;
|
|
121
|
+
|
|
93
122
|
/**
|
|
94
123
|
* Implements [get an encoding from a string `label`](https://encoding.spec.whatwg.org/#concept-encoding-get).
|
|
95
124
|
*
|
package/encoding.js
CHANGED
package/fallback/_utils.js
CHANGED
|
@@ -7,7 +7,8 @@ export function assert(condition, msg) {
|
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export function assertU8(arg) {
|
|
10
|
-
if (
|
|
10
|
+
if (arg && arg instanceof Uint8Array) return
|
|
11
|
+
throw new TypeError('Expected an Uint8Array')
|
|
11
12
|
}
|
|
12
13
|
|
|
13
14
|
// On arrays in heap (<= 64) it's cheaper to copy into a pooled buffer than lazy-create the ArrayBuffer storage
|
|
@@ -18,3 +19,42 @@ export const toBuf = (x) =>
|
|
|
18
19
|
|
|
19
20
|
export const E_STRING = 'Input is not a string'
|
|
20
21
|
export const E_STRICT_UNICODE = 'Input is not well-formed Unicode'
|
|
22
|
+
|
|
23
|
+
// Input is never pooled
|
|
24
|
+
export function fromUint8(arr, format) {
|
|
25
|
+
switch (format) {
|
|
26
|
+
case 'uint8':
|
|
27
|
+
if (arr.constructor !== Uint8Array) throw new Error('Unexpected')
|
|
28
|
+
return arr
|
|
29
|
+
case 'arraybuffer':
|
|
30
|
+
if (arr.byteLength !== arr.buffer.byteLength) throw new Error('Unexpected')
|
|
31
|
+
return arr.buffer
|
|
32
|
+
case 'buffer':
|
|
33
|
+
if (arr.length <= 64) return Buffer.from(arr)
|
|
34
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
throw new TypeError('Unexpected format')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Input can be pooled
|
|
41
|
+
export function fromBuffer(arr, format) {
|
|
42
|
+
switch (format) {
|
|
43
|
+
case 'uint8':
|
|
44
|
+
// byteOffset check is slightly faster and covers most pooling, so it comes first
|
|
45
|
+
if (arr.length <= 64 || arr.byteOffset !== 0 || arr.byteLength !== arr.buffer.byteLength) {
|
|
46
|
+
return new Uint8Array(arr)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength)
|
|
50
|
+
case 'arraybuffer':
|
|
51
|
+
return arr.buffer.byteLength === arr.byteLength
|
|
52
|
+
? arr.buffer
|
|
53
|
+
: arr.buffer.slice(arr.byteOffset, arr.byteOffset + arr.byteLength)
|
|
54
|
+
case 'buffer':
|
|
55
|
+
if (arr.constructor !== Buffer) throw new Error('Unexpected')
|
|
56
|
+
return arr
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
throw new TypeError('Unexpected format')
|
|
60
|
+
}
|
package/fallback/base32.js
CHANGED
|
@@ -4,10 +4,12 @@ import { encodeAscii, decodeAscii } from './latin1.js'
|
|
|
4
4
|
|
|
5
5
|
// See https://datatracker.ietf.org/doc/html/rfc4648
|
|
6
6
|
|
|
7
|
-
const
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
|
|
7
|
+
const BASE32_HELPERS = [{}, {}, {}]
|
|
8
|
+
const BASE32_ALPHABETS = [
|
|
9
|
+
[...'ABCDEFGHIJKLMNOPQRSTUVWXYZ234567'], // RFC 4648, #6
|
|
10
|
+
[...'0123456789ABCDEFGHIJKLMNOPQRSTUV'], // RFC 4648, #7
|
|
11
|
+
[...'0123456789ABCDEFGHJKMNPQRSTVWXYZ'], // Crockford, base (see extra below in fromMap)
|
|
12
|
+
]
|
|
11
13
|
|
|
12
14
|
export const E_CHAR = 'Invalid character in base32 input'
|
|
13
15
|
export const E_PADDING = 'Invalid base32 padding'
|
|
@@ -17,15 +19,15 @@ export const E_LAST = 'Invalid last chunk'
|
|
|
17
19
|
const useTemplates = isHermes // Faster on Hermes and JSC, but we use it only on Hermes
|
|
18
20
|
|
|
19
21
|
// We construct output by concatenating chars, this seems to be fine enough on modern JS engines
|
|
20
|
-
export function toBase32(arr,
|
|
22
|
+
export function toBase32(arr, mode, padding) {
|
|
21
23
|
assertU8(arr)
|
|
22
24
|
const fullChunks = Math.floor(arr.length / 5)
|
|
23
25
|
const fullChunksBytes = fullChunks * 5
|
|
24
26
|
let o = ''
|
|
25
27
|
let i = 0
|
|
26
28
|
|
|
27
|
-
const alphabet =
|
|
28
|
-
const helpers =
|
|
29
|
+
const alphabet = BASE32_ALPHABETS[mode]
|
|
30
|
+
const helpers = BASE32_HELPERS[mode]
|
|
29
31
|
if (!helpers.pairs) {
|
|
30
32
|
helpers.pairs = []
|
|
31
33
|
if (nativeDecoder) {
|
|
@@ -125,7 +127,7 @@ export function toBase32(arr, isBase32Hex, padding) {
|
|
|
125
127
|
// TODO: can this be optimized? This only affects non-Hermes barebone engines though
|
|
126
128
|
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
129
|
|
|
128
|
-
export function fromBase32(str,
|
|
130
|
+
export function fromBase32(str, mode) {
|
|
129
131
|
let inputLength = str.length
|
|
130
132
|
while (str[inputLength - 1] === '=') inputLength--
|
|
131
133
|
const paddingLength = str.length - inputLength
|
|
@@ -136,14 +138,21 @@ export function fromBase32(str, isBase32Hex) {
|
|
|
136
138
|
throw new SyntaxError(E_PADDING)
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
const alphabet =
|
|
140
|
-
const helpers =
|
|
141
|
+
const alphabet = BASE32_ALPHABETS[mode]
|
|
142
|
+
const helpers = BASE32_HELPERS[mode]
|
|
141
143
|
|
|
142
144
|
if (!helpers.fromMap) {
|
|
143
145
|
helpers.fromMap = new Int8Array(mapSize).fill(-1) // no regex input validation here, so we map all other bytes to -1 and recheck sign
|
|
146
|
+
const m = helpers.fromMap
|
|
144
147
|
alphabet.forEach((c, i) => {
|
|
145
|
-
|
|
148
|
+
m[c.charCodeAt(0)] = m[c.toLowerCase().charCodeAt(0)] = i
|
|
146
149
|
})
|
|
150
|
+
|
|
151
|
+
if (mode === 2) {
|
|
152
|
+
// Extra Crockford mapping
|
|
153
|
+
m[73] = m[76] = m[105] = m[108] = m[49] // ILil -> 1
|
|
154
|
+
m[79] = m[111] = m[48] // Oo -> 0
|
|
155
|
+
}
|
|
147
156
|
}
|
|
148
157
|
|
|
149
158
|
const m = helpers.fromMap
|
package/fallback/base58check.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
|
-
import { typedView } from '@exodus/bytes/array.js'
|
|
2
1
|
import { toBase58, fromBase58 } from '@exodus/bytes/base58.js'
|
|
3
|
-
import { assertU8 } from './_utils.js'
|
|
2
|
+
import { assertU8, fromUint8 } from './_utils.js'
|
|
4
3
|
|
|
5
4
|
const E_CHECKSUM = 'Invalid checksum'
|
|
6
5
|
|
|
@@ -10,7 +9,7 @@ function encodeWithChecksum(arr, checksum) {
|
|
|
10
9
|
// arr type in already validated in input
|
|
11
10
|
const res = new Uint8Array(arr.length + 4)
|
|
12
11
|
res.set(arr, 0)
|
|
13
|
-
res.set(checksum.
|
|
12
|
+
res.set(checksum.slice(0, 4), arr.length)
|
|
14
13
|
return toBase58(res)
|
|
15
14
|
}
|
|
16
15
|
|
|
@@ -18,7 +17,7 @@ function decodeWithChecksum(str) {
|
|
|
18
17
|
const arr = fromBase58(str) // checks input
|
|
19
18
|
const payloadSize = arr.length - 4
|
|
20
19
|
if (payloadSize < 0) throw new Error(E_CHECKSUM)
|
|
21
|
-
return [arr.
|
|
20
|
+
return [arr.slice(0, payloadSize), arr.slice(payloadSize)]
|
|
22
21
|
}
|
|
23
22
|
|
|
24
23
|
function assertChecksum(c, r) {
|
|
@@ -34,7 +33,7 @@ export const makeBase58check = (hashAlgo, hashAlgoSync) => {
|
|
|
34
33
|
async decode(str, format = 'uint8') {
|
|
35
34
|
const [payload, checksum] = decodeWithChecksum(str)
|
|
36
35
|
assertChecksum(checksum, await hashAlgo(payload))
|
|
37
|
-
return
|
|
36
|
+
return fromUint8(payload, format)
|
|
38
37
|
},
|
|
39
38
|
}
|
|
40
39
|
if (!hashAlgoSync) return apis
|
|
@@ -47,7 +46,7 @@ export const makeBase58check = (hashAlgo, hashAlgoSync) => {
|
|
|
47
46
|
decodeSync(str, format = 'uint8') {
|
|
48
47
|
const [payload, checksum] = decodeWithChecksum(str)
|
|
49
48
|
assertChecksum(checksum, hashAlgoSync(payload))
|
|
50
|
-
return
|
|
49
|
+
return fromUint8(payload, format)
|
|
51
50
|
},
|
|
52
51
|
}
|
|
53
52
|
}
|
package/fallback/encoding.js
CHANGED
|
@@ -3,7 +3,11 @@
|
|
|
3
3
|
|
|
4
4
|
import { utf16toString, utf16toStringLoose } from '@exodus/bytes/utf16.js'
|
|
5
5
|
import { utf8fromStringLoose, utf8toString, utf8toStringLoose } from '@exodus/bytes/utf8.js'
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
createSinglebyteDecoder,
|
|
8
|
+
latin1toString,
|
|
9
|
+
latin1fromString,
|
|
10
|
+
} from '@exodus/bytes/single-byte.js'
|
|
7
11
|
import labels from './encoding.labels.js'
|
|
8
12
|
import { fromSource, getBOMEncoding } from './encoding.api.js'
|
|
9
13
|
import { unfinishedBytes, mergePrefix } from './encoding.util.js'
|
|
@@ -210,9 +214,7 @@ export class TextEncoder {
|
|
|
210
214
|
|
|
211
215
|
encode(str = '') {
|
|
212
216
|
if (typeof str !== 'string') str = `${str}`
|
|
213
|
-
|
|
214
|
-
// match new Uint8Array (per spec), which is non-pooled
|
|
215
|
-
return res.byteOffset === 0 && res.length === res.buffer.byteLength ? res : res.slice(0)
|
|
217
|
+
return utf8fromStringLoose(str) // non-pooled
|
|
216
218
|
}
|
|
217
219
|
|
|
218
220
|
encodeInto(str, target) {
|
|
@@ -357,3 +359,11 @@ export function legacyHookDecode(input, fallbackEncoding = 'utf-8') {
|
|
|
357
359
|
|
|
358
360
|
return createSinglebyteDecoder(enc, true)(u8)
|
|
359
361
|
}
|
|
362
|
+
|
|
363
|
+
export function isomorphicDecode(input) {
|
|
364
|
+
return latin1toString(fromSource(input))
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function isomorphicEncode(str) {
|
|
368
|
+
return latin1fromString(str)
|
|
369
|
+
}
|
|
@@ -36,27 +36,28 @@ export function unfinishedBytes(u, len, enc) {
|
|
|
36
36
|
// otherwise returns a prefix with no unfinished bytes
|
|
37
37
|
export function mergePrefix(u, chunk, enc) {
|
|
38
38
|
if (u.length === 0) return chunk
|
|
39
|
+
const cl = chunk.length
|
|
39
40
|
if (u.length < 3) {
|
|
40
41
|
// No reason to bruteforce offsets, also it's possible this doesn't yet end the sequence
|
|
41
|
-
const a = new Uint8Array(
|
|
42
|
+
const a = new Uint8Array(cl + u.length)
|
|
42
43
|
a.set(chunk)
|
|
43
|
-
a.set(u,
|
|
44
|
+
a.set(u, cl)
|
|
44
45
|
return a
|
|
45
46
|
}
|
|
46
47
|
|
|
47
48
|
// Slice off a small portion of u into prefix chunk so we can decode them separately without extending array size
|
|
48
|
-
const t = new Uint8Array(
|
|
49
|
+
const t = new Uint8Array(cl + 3) // We have 1-3 bytes and need 1-3 more bytes
|
|
49
50
|
t.set(chunk)
|
|
50
|
-
t.set(u.subarray(0, 3),
|
|
51
|
+
t.set(u.subarray(0, 3), cl)
|
|
51
52
|
|
|
52
53
|
// Stop at the first offset where unfinished bytes reaches 0 or fits into u
|
|
53
54
|
// If that doesn't happen (u too short), just concat chunk and u completely (above)
|
|
54
55
|
for (let i = 1; i <= 3; i++) {
|
|
55
|
-
const unfinished = unfinishedBytes(t,
|
|
56
|
+
const unfinished = unfinishedBytes(t, cl + i, enc) // 0-3
|
|
56
57
|
if (unfinished <= i) {
|
|
57
58
|
// Always reachable at 3, but we still need 'unfinished' value for it
|
|
58
59
|
const add = i - unfinished // 0-3
|
|
59
|
-
return add > 0 ? t.subarray(0,
|
|
60
|
+
return add > 0 ? t.subarray(0, cl + add) : chunk
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
|
package/fallback/hex.js
CHANGED
|
@@ -73,7 +73,7 @@ export function fromHex(str) {
|
|
|
73
73
|
}
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
const codes = encodeAscii(str, E_HEX)
|
|
76
|
+
const codes = encodeAscii(str, E_HEX) // aligned
|
|
77
77
|
const codes16 = new Uint16Array(codes.buffer, codes.byteOffset, codes.byteLength / 2)
|
|
78
78
|
let i = 0
|
|
79
79
|
for (const last3 = length - 3; i < last3; i += 4) {
|
package/fallback/latin1.js
CHANGED
|
@@ -140,6 +140,7 @@ export const encodeAscii = useEncodeInto
|
|
|
140
140
|
: nativeBuffer
|
|
141
141
|
? (str, ERR) => {
|
|
142
142
|
// TextEncoder is slow on Node.js 24 / 25 (was ok on 22)
|
|
143
|
+
// Node.js Buffer.from always returns 8-byte aligned allocations, this is safe for e.g. conversion to Uint32Array
|
|
143
144
|
const codes = nativeBuffer.from(str, 'utf8') // ascii/latin1 coerces, we need to check
|
|
144
145
|
if (codes.length !== str.length) throw new SyntaxError(ERR) // non-ascii
|
|
145
146
|
return new Uint8Array(codes.buffer, codes.byteOffset, codes.byteLength)
|
package/fallback/multi-byte.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { E_STRING } from './_utils.js'
|
|
2
|
-
import {
|
|
2
|
+
import { nativeEncoder } from './platform.js'
|
|
3
|
+
import { asciiPrefix, decodeAscii, decodeLatin1, decodeUCS2 } from './latin1.js'
|
|
3
4
|
import { getTable } from './multi-byte.table.js'
|
|
4
5
|
|
|
5
6
|
export const E_STRICT = 'Input is not well-formed for this encoding'
|
|
@@ -776,10 +777,9 @@ export function multibyteEncoder(enc, onError) {
|
|
|
776
777
|
if (iso2022jp && !katakana) katakana = getTable('iso-2022-jp-katakana')
|
|
777
778
|
return (str) => {
|
|
778
779
|
if (typeof str !== 'string') throw new TypeError(E_STRING)
|
|
779
|
-
if (ascii && !NON_LATIN.test(str)) {
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
} catch {}
|
|
780
|
+
if (ascii && nativeEncoder && !NON_LATIN.test(str)) {
|
|
781
|
+
const u8 = nativeEncoder.encode(str)
|
|
782
|
+
if (u8.length === str.length) return u8
|
|
783
783
|
}
|
|
784
784
|
|
|
785
785
|
const length = str.length
|
|
@@ -957,6 +957,6 @@ export function multibyteEncoder(enc, onError) {
|
|
|
957
957
|
}
|
|
958
958
|
}
|
|
959
959
|
|
|
960
|
-
return i === u8.length ? u8 : u8.
|
|
960
|
+
return i === u8.length ? u8 : u8.slice(0, i)
|
|
961
961
|
}
|
|
962
962
|
}
|
package/hex.d.ts
CHANGED
|
@@ -31,5 +31,6 @@ export function toHex(arr: Uint8Array): string;
|
|
|
31
31
|
* @returns The decoded bytes
|
|
32
32
|
*/
|
|
33
33
|
export function fromHex(string: string, format?: 'uint8'): Uint8ArrayBuffer;
|
|
34
|
+
export function fromHex(string: string, format: 'arraybuffer'): ArrayBuffer;
|
|
34
35
|
export function fromHex(string: string, format: 'buffer'): Buffer;
|
|
35
|
-
export function fromHex(string: string, format?: OutputFormat): Uint8ArrayBuffer | Buffer;
|
|
36
|
+
export function fromHex(string: string, format?: OutputFormat): Uint8ArrayBuffer | ArrayBuffer | Buffer;
|
package/hex.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { assertU8 } from './fallback/_utils.js'
|
|
1
|
+
import { assertU8, fromUint8 } from './fallback/_utils.js'
|
|
3
2
|
import * as js from './fallback/hex.js'
|
|
4
3
|
|
|
5
4
|
const { toHex: webHex } = Uint8Array.prototype // Modern engines have this
|
|
@@ -13,5 +12,5 @@ export function toHex(arr) {
|
|
|
13
12
|
|
|
14
13
|
// Unlike Buffer.from(), throws on invalid input
|
|
15
14
|
export const fromHex = Uint8Array.fromHex
|
|
16
|
-
? (str, format = 'uint8') =>
|
|
17
|
-
: (str, format = 'uint8') =>
|
|
15
|
+
? (str, format = 'uint8') => fromUint8(Uint8Array.fromHex(str), format)
|
|
16
|
+
: (str, format = 'uint8') => fromUint8(js.fromHex(str), format)
|
package/hex.node.js
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { assertU8, E_STRING } from './fallback/_utils.js'
|
|
1
|
+
import { assertU8, fromBuffer, fromUint8, E_STRING } from './fallback/_utils.js'
|
|
3
2
|
import { E_HEX } from './fallback/hex.js'
|
|
4
3
|
|
|
5
4
|
if (Buffer.TYPED_ARRAY_SUPPORT) throw new Error('Unexpected Buffer polyfill')
|
|
@@ -17,12 +16,22 @@ export function toHex(arr) {
|
|
|
17
16
|
|
|
18
17
|
// Unlike Buffer.from(), throws on invalid input
|
|
19
18
|
export const fromHex = Uint8Array.fromHex
|
|
20
|
-
? (str, format = 'uint8') =>
|
|
19
|
+
? (str, format = 'uint8') => fromUint8(Uint8Array.fromHex(str), format)
|
|
21
20
|
: (str, format = 'uint8') => {
|
|
22
21
|
if (typeof str !== 'string') throw new TypeError(E_STRING)
|
|
23
22
|
if (str.length % 2 !== 0) throw new SyntaxError(E_HEX)
|
|
24
23
|
if (denoBug && /[^\dA-Fa-f]/.test(str)) throw new SyntaxError(E_HEX)
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
|
|
25
|
+
// 64 bytes or less, in heap
|
|
26
|
+
if (str.length <= 128) {
|
|
27
|
+
const buf = Buffer.from(str, 'hex')
|
|
28
|
+
if (buf.length * 2 !== str.length) throw new SyntaxError(E_HEX)
|
|
29
|
+
return fromBuffer(buf, format)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const length = str.length / 2
|
|
33
|
+
const buf = format === 'buffer' ? Buffer.allocUnsafe(length) : Buffer.allocUnsafeSlow(length) // avoid pooling
|
|
34
|
+
const count = buf.hexWrite(str, 0, length)
|
|
35
|
+
if (count !== length) throw new SyntaxError(E_HEX) // will stop on first non-hex character, so we can just validate length
|
|
36
|
+
return fromBuffer(buf, format)
|
|
28
37
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bytes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.1",
|
|
4
4
|
"description": "Various operations on Uint8Array data",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"encoding",
|
|
@@ -273,7 +273,7 @@
|
|
|
273
273
|
"buffer": "^6.0.3",
|
|
274
274
|
"c8": "^10.1.3",
|
|
275
275
|
"decode-utf8": "^1.0.1",
|
|
276
|
-
"electron": "
|
|
276
|
+
"electron": "39.4.0",
|
|
277
277
|
"encode-utf8": "^2.0.0",
|
|
278
278
|
"esbuild": "^0.27.3",
|
|
279
279
|
"eslint": "^8.44.0",
|
|
@@ -291,7 +291,7 @@
|
|
|
291
291
|
"utf8": "^3.0.0",
|
|
292
292
|
"web-streams-polyfill": "^4.2.0",
|
|
293
293
|
"wif": "^5.0.0",
|
|
294
|
-
"workerd": "^1.
|
|
294
|
+
"workerd": "^1.20260301.1"
|
|
295
295
|
},
|
|
296
296
|
"prettier": "@exodus/prettier",
|
|
297
297
|
"packageManager": "pnpm@10.12.1+sha256.889bac470ec93ccc3764488a19d6ba8f9c648ad5e50a9a6e4be3768a5de387a3"
|
package/single-byte.d.ts
CHANGED
|
@@ -108,6 +108,10 @@ export function createSinglebyteEncoder(
|
|
|
108
108
|
* > This is different from `new TextDecoder('iso-8859-1')` and `new TextDecoder('latin1')`, as those
|
|
109
109
|
* > alias to `new TextDecoder('windows-1252')`.
|
|
110
110
|
*
|
|
111
|
+
* Prefer using `isomorphicDecode()` from `@exodus/bytes/encoding.js` or `@exodus/bytes/encoding-lite.js`,
|
|
112
|
+
* which is identical to this but allows more input types.
|
|
113
|
+
*
|
|
114
|
+
* @deprecated Use `import { isomorphicDecode } from '@exodus/bytes/encoding-lite.js'`
|
|
111
115
|
* @param arr - The bytes to decode
|
|
112
116
|
* @returns The decoded string
|
|
113
117
|
*/
|
|
@@ -123,6 +127,9 @@ export function latin1toString(arr: Uint8Array): string;
|
|
|
123
127
|
* const latin1fromString = createSinglebyteEncoder('iso-8859-1', { mode: 'fatal' })
|
|
124
128
|
* ```
|
|
125
129
|
*
|
|
130
|
+
* Prefer using `isomorphicEncode()` from `@exodus/bytes/encoding.js` or `@exodus/bytes/encoding-lite.js`.
|
|
131
|
+
*
|
|
132
|
+
* @deprecated Use `import { isomorphicEncode } from '@exodus/bytes/encoding-lite.js'`
|
|
126
133
|
* @param string - The string to encode
|
|
127
134
|
* @returns The encoded bytes
|
|
128
135
|
*/
|