@exodus/bytes 1.14.1 → 1.15.0
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/base32.d.ts +31 -2
- package/base32.js +18 -20
- package/base58.d.ts +4 -2
- package/base58.js +3 -4
- package/base58check.d.ts +8 -4
- package/base64.d.ts +9 -3
- package/base64.js +13 -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 +37 -0
- 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/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'
|
|
@@ -96,7 +95,7 @@ function fromBase64common(str, isBase64url, padding, format, rest) {
|
|
|
96
95
|
throw new TypeError('Invalid padding option')
|
|
97
96
|
}
|
|
98
97
|
|
|
99
|
-
return
|
|
98
|
+
return fromBase64impl(str, isBase64url, padding, format)
|
|
100
99
|
}
|
|
101
100
|
|
|
102
101
|
// ASCII whitespace is U+0009 TAB, U+000A LF, U+000C FF, U+000D CR, or U+0020 SPACE
|
|
@@ -114,7 +113,7 @@ function noWhitespaceSeen(str, arr) {
|
|
|
114
113
|
let fromBase64impl
|
|
115
114
|
if (Uint8Array.fromBase64) {
|
|
116
115
|
// 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) => {
|
|
116
|
+
fromBase64impl = (str, isBase64url, padding, format) => {
|
|
118
117
|
const alphabet = isBase64url ? 'base64url' : 'base64'
|
|
119
118
|
|
|
120
119
|
let arr
|
|
@@ -136,20 +135,22 @@ if (Uint8Array.fromBase64) {
|
|
|
136
135
|
// We don't allow whitespace in input, but that can be rechecked based on output length
|
|
137
136
|
// All other chars are checked natively
|
|
138
137
|
if (!noWhitespaceSeen(str, arr)) throw new SyntaxError(E_CHAR)
|
|
139
|
-
return arr
|
|
138
|
+
return fromUint8(arr, format)
|
|
140
139
|
}
|
|
141
140
|
} else if (haveNativeBuffer) {
|
|
142
|
-
fromBase64impl = (str, isBase64url, padding) => {
|
|
143
|
-
const
|
|
141
|
+
fromBase64impl = (str, isBase64url, padding, format) => {
|
|
142
|
+
const size = Buffer.byteLength(str, 'base64')
|
|
143
|
+
const arr = Buffer.allocUnsafeSlow(size) // non-pooled
|
|
144
|
+
if (arr.base64Write(str) !== size) throw new SyntaxError(E_PADDING)
|
|
144
145
|
// Rechecking by re-encoding is cheaper than regexes on Node.js
|
|
145
146
|
const got = isBase64url ? maybeUnpad(str, padding === false) : maybePad(str, padding !== true)
|
|
146
147
|
const valid = isBase64url ? arr.base64urlSlice(0, arr.length) : arr.base64Slice(0, arr.length)
|
|
147
148
|
if (got !== valid) throw new SyntaxError(E_PADDING)
|
|
148
|
-
return arr // fully checked
|
|
149
|
+
return fromBuffer(arr, format) // fully checked
|
|
149
150
|
}
|
|
150
151
|
} else if (shouldUseAtob) {
|
|
151
152
|
// atob is faster than manual parsing on Hermes
|
|
152
|
-
fromBase64impl = (str, isBase64url, padding) => {
|
|
153
|
+
fromBase64impl = (str, isBase64url, padding, format) => {
|
|
153
154
|
let arr
|
|
154
155
|
if (isBase64url) {
|
|
155
156
|
if (/[\t\n\f\r +/]/.test(str)) throw new SyntaxError(E_CHAR) // atob verifies other invalid input
|
|
@@ -171,8 +172,9 @@ if (Uint8Array.fromBase64) {
|
|
|
171
172
|
if (expected !== end) throw new SyntaxError(E_LAST)
|
|
172
173
|
}
|
|
173
174
|
|
|
174
|
-
return arr
|
|
175
|
+
return fromUint8(arr, format)
|
|
175
176
|
}
|
|
176
177
|
} else {
|
|
177
|
-
fromBase64impl = (str, isBase64url, padding
|
|
178
|
+
fromBase64impl = (str, isBase64url, padding, format) =>
|
|
179
|
+
fromUint8(js.fromBase64(str, isBase64url), format) // validated in js
|
|
178
180
|
}
|
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
|
@@ -18,3 +18,40 @@ export const toBuf = (x) =>
|
|
|
18
18
|
|
|
19
19
|
export const E_STRING = 'Input is not a string'
|
|
20
20
|
export const E_STRICT_UNICODE = 'Input is not well-formed Unicode'
|
|
21
|
+
|
|
22
|
+
// Input is never pooled
|
|
23
|
+
export function fromUint8(arr, format) {
|
|
24
|
+
switch (format) {
|
|
25
|
+
case 'uint8':
|
|
26
|
+
if (arr.constructor !== Uint8Array) throw new Error('Unexpected')
|
|
27
|
+
return arr
|
|
28
|
+
case 'arraybuffer':
|
|
29
|
+
if (arr.byteLength !== arr.buffer.byteLength) throw new Error('Unexpected')
|
|
30
|
+
return arr.buffer
|
|
31
|
+
case 'buffer':
|
|
32
|
+
if (arr.length <= 64) return Buffer.from(arr)
|
|
33
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength)
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
throw new TypeError('Unexpected format')
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Input can be pooled
|
|
40
|
+
export function fromBuffer(arr, format) {
|
|
41
|
+
switch (format) {
|
|
42
|
+
case 'uint8':
|
|
43
|
+
// byteOffset check is slightly faster and covers most pooling, so it comes first
|
|
44
|
+
if (arr.length <= 64 || arr.byteOffset !== 0 || arr.byteLength !== arr.buffer.byteLength) {
|
|
45
|
+
return new Uint8Array(arr)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength)
|
|
49
|
+
case 'arraybuffer':
|
|
50
|
+
return fromBuffer(arr, 'uint8').buffer
|
|
51
|
+
case 'buffer':
|
|
52
|
+
if (arr.constructor !== Buffer) throw new Error('Unexpected')
|
|
53
|
+
return arr
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
throw new TypeError('Unexpected format')
|
|
57
|
+
}
|
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/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.0",
|
|
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
|
*/
|
package/single-byte.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assertU8, E_STRING } from './fallback/_utils.js'
|
|
2
2
|
import { nativeDecoderLatin1, nativeEncoder } from './fallback/platform.js'
|
|
3
|
-
import {
|
|
3
|
+
import { encodeAsciiPrefix, encodeLatin1 } from './fallback/latin1.js'
|
|
4
4
|
import { assertEncoding, encodingDecoder, encodeMap, E_STRICT } from './fallback/single-byte.js'
|
|
5
5
|
|
|
6
6
|
const { TextDecoder, btoa } = globalThis
|
|
@@ -90,37 +90,38 @@ function encode(s, m) {
|
|
|
90
90
|
// fromBase64+btoa path is faster on everything where fromBase64 is fast
|
|
91
91
|
const useLatin1btoa = Uint8Array.fromBase64 && btoa
|
|
92
92
|
|
|
93
|
+
export function latin1fromString(s) {
|
|
94
|
+
if (typeof s !== 'string') throw new TypeError(E_STRING)
|
|
95
|
+
// max limit is to not produce base64 strings that are too long
|
|
96
|
+
if (useLatin1btoa && s.length >= 1024 && s.length < 1e8) {
|
|
97
|
+
try {
|
|
98
|
+
return Uint8Array.fromBase64(btoa(s)) // fails on non-latin1
|
|
99
|
+
} catch {
|
|
100
|
+
throw new TypeError(E_STRICT)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
if (NON_LATIN.test(s)) throw new TypeError(E_STRICT)
|
|
105
|
+
return encodeLatin1(s)
|
|
106
|
+
}
|
|
107
|
+
|
|
93
108
|
export function createSinglebyteEncoder(encoding, { mode = 'fatal' } = {}) {
|
|
94
109
|
// TODO: replacement, truncate (replacement will need varying length)
|
|
95
110
|
if (mode !== 'fatal') throw new Error('Unsupported mode')
|
|
111
|
+
if (encoding === 'iso-8859-1') return latin1fromString
|
|
96
112
|
const m = encodeMap(encoding) // asserts
|
|
97
|
-
const isLatin1 = encoding === 'iso-8859-1'
|
|
98
113
|
|
|
99
114
|
// No single-byte encoder produces surrogate pairs, so any surrogate is invalid
|
|
100
115
|
// This needs special treatment only to decide how many replacement chars to output, one or two
|
|
101
116
|
// Not much use in running isWellFormed, most likely cause of error is unmapped chars, not surrogate pairs
|
|
102
117
|
return (s) => {
|
|
103
118
|
if (typeof s !== 'string') throw new TypeError(E_STRING)
|
|
104
|
-
if (isLatin1) {
|
|
105
|
-
// max limit is to not produce base64 strings that are too long
|
|
106
|
-
if (useLatin1btoa && s.length >= 1024 && s.length < 1e8) {
|
|
107
|
-
try {
|
|
108
|
-
return Uint8Array.fromBase64(btoa(s)) // fails on non-latin1
|
|
109
|
-
} catch {
|
|
110
|
-
throw new TypeError(E_STRICT)
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (NON_LATIN.test(s)) throw new TypeError(E_STRICT)
|
|
115
|
-
return encodeLatin1(s)
|
|
116
|
-
}
|
|
117
119
|
|
|
118
120
|
// Instead of an ASCII regex check, encode optimistically - this is faster
|
|
119
121
|
// Check for 8-bit string with a regex though, this is instant on 8-bit strings so doesn't hurt the ASCII fast path
|
|
120
122
|
if (nativeEncoder && !NON_LATIN.test(s)) {
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
} catch {}
|
|
123
|
+
const u8 = nativeEncoder.encode(s)
|
|
124
|
+
if (u8.length === s.length) return u8
|
|
124
125
|
}
|
|
125
126
|
|
|
126
127
|
const res = encode(s, m)
|
|
@@ -130,6 +131,5 @@ export function createSinglebyteEncoder(encoding, { mode = 'fatal' } = {}) {
|
|
|
130
131
|
}
|
|
131
132
|
|
|
132
133
|
export const latin1toString = /* @__PURE__ */ createSinglebyteDecoder('iso-8859-1')
|
|
133
|
-
export const latin1fromString = /* @__PURE__ */ createSinglebyteEncoder('iso-8859-1')
|
|
134
134
|
export const windows1252toString = /* @__PURE__ */ createSinglebyteDecoder('windows-1252')
|
|
135
135
|
export const windows1252fromString = /* @__PURE__ */ createSinglebyteEncoder('windows-1252')
|