@exodus/bytes 1.2.0 → 1.3.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 +22 -4
- package/base58check.js +4 -54
- package/base58check.node.js +14 -0
- package/encoding-lite.js +1 -0
- package/encoding.js +1 -0
- package/fallback/base58check.js +53 -0
- package/fallback/encoding.js +13 -0
- package/package.json +7 -2
- package/utf16.js +3 -2
- package/utf16.node.js +8 -7
package/README.md
CHANGED
|
@@ -161,6 +161,8 @@ Same as `windows1252toString = createSinglebyteDecoder('windows-1252')`.
|
|
|
161
161
|
|
|
162
162
|
### `@exodus/bytes/base58check.js`
|
|
163
163
|
|
|
164
|
+
On non-Node.js, requires peer dependency [@exodus/crypto](https://www.npmjs.com/package/@exodus/crypto) to be installed.
|
|
165
|
+
|
|
164
166
|
##### `async toBase58check(arr)`
|
|
165
167
|
##### `toBase58checkSync(arr)`
|
|
166
168
|
##### `async fromBase58check(str, format = 'uint8')`
|
|
@@ -185,7 +187,7 @@ some [hooks](https://encoding.spec.whatwg.org/#specification-hooks) (see below).
|
|
|
185
187
|
import { TextDecoder, TextDecoder } from '@exodus/bytes/encoding.js'
|
|
186
188
|
|
|
187
189
|
// Hooks for standards
|
|
188
|
-
import { getBOMEncoding, legacyHookDecode, normalizeEncoding } from '@exodus/bytes/encoding.js'
|
|
190
|
+
import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding.js'
|
|
189
191
|
```
|
|
190
192
|
|
|
191
193
|
#### `new TextDecoder(label = 'utf-8', { fatal = false, ignoreBOM = false })`
|
|
@@ -196,10 +198,19 @@ import { getBOMEncoding, legacyHookDecode, normalizeEncoding } from '@exodus/byt
|
|
|
196
198
|
|
|
197
199
|
[TextEncoder](https://encoding.spec.whatwg.org/#interface-textdecoder) implementation/polyfill.
|
|
198
200
|
|
|
199
|
-
#### `
|
|
201
|
+
#### `labelToName(label)`
|
|
200
202
|
|
|
201
203
|
Implements [get an encoding from a string `label`](https://encoding.spec.whatwg.org/#concept-encoding-get).
|
|
202
204
|
|
|
205
|
+
Converts an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
|
|
206
|
+
as a case-sensitive string.
|
|
207
|
+
|
|
208
|
+
If an encoding with that label does not exist, returns `null`.
|
|
209
|
+
|
|
210
|
+
All encoding names are also valid labels for corresponding encodings.
|
|
211
|
+
|
|
212
|
+
#### `normalizeEncoding(label)`
|
|
213
|
+
|
|
203
214
|
Converts an encoding [label](https://encoding.spec.whatwg.org/#names-and-labels) to its name,
|
|
204
215
|
as an ASCII-lowercased string.
|
|
205
216
|
|
|
@@ -211,6 +222,11 @@ except that it:
|
|
|
211
222
|
[labels](https://encoding.spec.whatwg.org/#ref-for-replacement%E2%91%A1)
|
|
212
223
|
2. Does not throw for invalid labels and instead returns `null`
|
|
213
224
|
|
|
225
|
+
It is identical to:
|
|
226
|
+
```js
|
|
227
|
+
labelToName(label)?.toLowerCase() ?? null
|
|
228
|
+
```
|
|
229
|
+
|
|
214
230
|
All encoding names are also valid labels for corresponding encodings.
|
|
215
231
|
|
|
216
232
|
#### `getBOMEncoding(input)`
|
|
@@ -252,7 +268,7 @@ new TextDecoder(getBOMEncoding(input) ?? fallbackEncoding).decode(input)
|
|
|
252
268
|
import { TextDecoder, TextDecoder } from '@exodus/bytes/encoding-lite.js'
|
|
253
269
|
|
|
254
270
|
// Hooks for standards
|
|
255
|
-
import { getBOMEncoding, legacyHookDecode, normalizeEncoding } from '@exodus/bytes/encoding-lite.js'
|
|
271
|
+
import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding-lite.js'
|
|
256
272
|
```
|
|
257
273
|
|
|
258
274
|
The exact same exports as `@exodus/bytes/encoding.js` are also exported as
|
|
@@ -264,7 +280,7 @@ and their [labels](https://encoding.spec.whatwg.org/#names-and-labels) when used
|
|
|
264
280
|
|
|
265
281
|
Legacy single-byte encodingds are loaded by default in both cases.
|
|
266
282
|
|
|
267
|
-
`TextEncoder` and hooks for standards (including `normalizeEncoding`) do not have any behavior
|
|
283
|
+
`TextEncoder` and hooks for standards (including `labelToName` / `normalizeEncoding`) do not have any behavior
|
|
268
284
|
differences in the lite version and support full range if inputs.
|
|
269
285
|
|
|
270
286
|
To avoid inconsistencies, the exported classes and methods are exactly the same objects.
|
|
@@ -275,6 +291,7 @@ To avoid inconsistencies, the exported classes and methods are exactly the same
|
|
|
275
291
|
TextDecoder: [class TextDecoder],
|
|
276
292
|
TextEncoder: [class TextEncoder],
|
|
277
293
|
getBOMEncoding: [Function: getBOMEncoding],
|
|
294
|
+
labelToName: [Function: labelToName],
|
|
278
295
|
legacyHookDecode: [Function: legacyHookDecode],
|
|
279
296
|
normalizeEncoding: [Function: normalizeEncoding]
|
|
280
297
|
}
|
|
@@ -287,6 +304,7 @@ Error: Legacy multi-byte encodings are disabled in /encoding-lite.js, use /encod
|
|
|
287
304
|
TextDecoder: [class TextDecoder],
|
|
288
305
|
TextEncoder: [class TextEncoder],
|
|
289
306
|
getBOMEncoding: [Function: getBOMEncoding],
|
|
307
|
+
labelToName: [Function: labelToName],
|
|
290
308
|
legacyHookDecode: [Function: legacyHookDecode],
|
|
291
309
|
normalizeEncoding: [Function: normalizeEncoding]
|
|
292
310
|
}
|
package/base58check.js
CHANGED
|
@@ -1,63 +1,12 @@
|
|
|
1
|
-
import { typedView } from './array.js'
|
|
2
|
-
import { assertUint8 } from './assert.js'
|
|
3
|
-
import { toBase58, fromBase58 } from './base58.js'
|
|
4
1
|
import { hashSync } from '@exodus/crypto/hash' // eslint-disable-line @exodus/import/no-deprecated
|
|
2
|
+
import { makeBase58check } from './fallback/base58check.js'
|
|
5
3
|
|
|
6
4
|
// Note: while API is async, we use hashSync for now until we improve webcrypto perf for hash256
|
|
7
5
|
// Inputs to base58 are typically very small, and that makes a difference
|
|
8
6
|
|
|
9
|
-
const E_CHECKSUM = 'Invalid checksum'
|
|
10
|
-
|
|
11
|
-
// checksum length is 4, i.e. only the first 4 bytes of the hash are used
|
|
12
|
-
|
|
13
|
-
function encodeWithChecksum(arr, checksum) {
|
|
14
|
-
// arr type in already validated in input
|
|
15
|
-
const res = new Uint8Array(arr.length + 4)
|
|
16
|
-
res.set(arr, 0)
|
|
17
|
-
res.set(checksum.subarray(0, 4), arr.length)
|
|
18
|
-
return toBase58(res)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function decodeWithChecksum(str) {
|
|
22
|
-
const arr = fromBase58(str) // checks input
|
|
23
|
-
const payloadSize = arr.length - 4
|
|
24
|
-
if (payloadSize < 0) throw new Error(E_CHECKSUM)
|
|
25
|
-
return [arr.subarray(0, payloadSize), arr.subarray(payloadSize)]
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function assertChecksum(c, r) {
|
|
29
|
-
if ((c[0] ^ r[0]) | (c[1] ^ r[1]) | (c[2] ^ r[2]) | (c[3] ^ r[3])) throw new Error(E_CHECKSUM)
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const makeBase58check = (hashAlgo, hashAlgoSync) => {
|
|
33
|
-
const apis = {
|
|
34
|
-
async encode(arr) {
|
|
35
|
-
assertUint8(arr)
|
|
36
|
-
return encodeWithChecksum(arr, await hashAlgo(arr))
|
|
37
|
-
},
|
|
38
|
-
async decode(str, format = 'uint8') {
|
|
39
|
-
const [payload, checksum] = decodeWithChecksum(str)
|
|
40
|
-
assertChecksum(checksum, await hashAlgo(payload))
|
|
41
|
-
return typedView(payload, format)
|
|
42
|
-
},
|
|
43
|
-
}
|
|
44
|
-
if (!hashAlgoSync) return apis
|
|
45
|
-
return {
|
|
46
|
-
...apis,
|
|
47
|
-
encodeSync(arr) {
|
|
48
|
-
assertUint8(arr)
|
|
49
|
-
return encodeWithChecksum(arr, hashAlgoSync(arr))
|
|
50
|
-
},
|
|
51
|
-
decodeSync(str, format = 'uint8') {
|
|
52
|
-
const [payload, checksum] = decodeWithChecksum(str)
|
|
53
|
-
assertChecksum(checksum, hashAlgoSync(payload))
|
|
54
|
-
return typedView(payload, format)
|
|
55
|
-
},
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
7
|
// eslint-disable-next-line @exodus/import/no-deprecated
|
|
60
|
-
const
|
|
8
|
+
const sha256 = (x) => hashSync('sha256', x, 'uint8')
|
|
9
|
+
const hash256sync = (x) => sha256(sha256(x))
|
|
61
10
|
const hash256 = hash256sync // See note at the top
|
|
62
11
|
const {
|
|
63
12
|
encode: toBase58check,
|
|
@@ -66,4 +15,5 @@ const {
|
|
|
66
15
|
decodeSync: fromBase58checkSync,
|
|
67
16
|
} = makeBase58check(hash256, hash256sync)
|
|
68
17
|
|
|
18
|
+
export { makeBase58check } from './fallback/base58check.js'
|
|
69
19
|
export { toBase58check, fromBase58check, toBase58checkSync, fromBase58checkSync }
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { hash } from 'node:crypto'
|
|
2
|
+
import { makeBase58check } from './fallback/base58check.js'
|
|
3
|
+
|
|
4
|
+
const sha256 = (x) => hash('sha256', x, 'buffer')
|
|
5
|
+
const hash256 = (x) => sha256(sha256(x))
|
|
6
|
+
const {
|
|
7
|
+
encode: toBase58check,
|
|
8
|
+
decode: fromBase58check,
|
|
9
|
+
encodeSync: toBase58checkSync,
|
|
10
|
+
decodeSync: fromBase58checkSync,
|
|
11
|
+
} = makeBase58check(hash256, hash256)
|
|
12
|
+
|
|
13
|
+
export { makeBase58check } from './fallback/base58check.js'
|
|
14
|
+
export { toBase58check, fromBase58check, toBase58checkSync, fromBase58checkSync }
|
package/encoding-lite.js
CHANGED
package/encoding.js
CHANGED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { typedView } from '@exodus/bytes/array.js'
|
|
2
|
+
import { toBase58, fromBase58 } from '@exodus/bytes/base58.js'
|
|
3
|
+
import { assertUint8 } from '../assert.js'
|
|
4
|
+
|
|
5
|
+
const E_CHECKSUM = 'Invalid checksum'
|
|
6
|
+
|
|
7
|
+
// checksum length is 4, i.e. only the first 4 bytes of the hash are used
|
|
8
|
+
|
|
9
|
+
function encodeWithChecksum(arr, checksum) {
|
|
10
|
+
// arr type in already validated in input
|
|
11
|
+
const res = new Uint8Array(arr.length + 4)
|
|
12
|
+
res.set(arr, 0)
|
|
13
|
+
res.set(checksum.subarray(0, 4), arr.length)
|
|
14
|
+
return toBase58(res)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function decodeWithChecksum(str) {
|
|
18
|
+
const arr = fromBase58(str) // checks input
|
|
19
|
+
const payloadSize = arr.length - 4
|
|
20
|
+
if (payloadSize < 0) throw new Error(E_CHECKSUM)
|
|
21
|
+
return [arr.subarray(0, payloadSize), arr.subarray(payloadSize)]
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function assertChecksum(c, r) {
|
|
25
|
+
if ((c[0] ^ r[0]) | (c[1] ^ r[1]) | (c[2] ^ r[2]) | (c[3] ^ r[3])) throw new Error(E_CHECKSUM)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export const makeBase58check = (hashAlgo, hashAlgoSync) => {
|
|
29
|
+
const apis = {
|
|
30
|
+
async encode(arr) {
|
|
31
|
+
assertUint8(arr)
|
|
32
|
+
return encodeWithChecksum(arr, await hashAlgo(arr))
|
|
33
|
+
},
|
|
34
|
+
async decode(str, format = 'uint8') {
|
|
35
|
+
const [payload, checksum] = decodeWithChecksum(str)
|
|
36
|
+
assertChecksum(checksum, await hashAlgo(payload))
|
|
37
|
+
return typedView(payload, format)
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
if (!hashAlgoSync) return apis
|
|
41
|
+
return {
|
|
42
|
+
...apis,
|
|
43
|
+
encodeSync(arr) {
|
|
44
|
+
assertUint8(arr)
|
|
45
|
+
return encodeWithChecksum(arr, hashAlgoSync(arr))
|
|
46
|
+
},
|
|
47
|
+
decodeSync(str, format = 'uint8') {
|
|
48
|
+
const [payload, checksum] = decodeWithChecksum(str)
|
|
49
|
+
assertChecksum(checksum, hashAlgoSync(payload))
|
|
50
|
+
return typedView(payload, format)
|
|
51
|
+
},
|
|
52
|
+
}
|
|
53
|
+
}
|
package/fallback/encoding.js
CHANGED
|
@@ -288,3 +288,16 @@ export function legacyHookDecode(input, fallbackEncoding = 'utf-8') {
|
|
|
288
288
|
|
|
289
289
|
return createSinglebyteDecoder(enc, true)(u8)
|
|
290
290
|
}
|
|
291
|
+
|
|
292
|
+
const uppercasePrefixes = new Set(['utf', 'iso', 'koi', 'euc', 'ibm', 'gbk'])
|
|
293
|
+
|
|
294
|
+
// Unlike normalizeEncoding, case-sensitive
|
|
295
|
+
// https://encoding.spec.whatwg.org/#names-and-labels
|
|
296
|
+
export function labelToName(label) {
|
|
297
|
+
const enc = normalizeEncoding(label)
|
|
298
|
+
if (!enc) return enc
|
|
299
|
+
if (uppercasePrefixes.has(enc.slice(0, 3))) return enc.toUpperCase()
|
|
300
|
+
if (enc === 'big5') return 'Big5'
|
|
301
|
+
if (enc === 'shift_jis') return 'Shift_JIS'
|
|
302
|
+
return enc
|
|
303
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bytes",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"description": "Various operations on Uint8Array data",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"lint": "eslint .",
|
|
@@ -45,6 +45,7 @@
|
|
|
45
45
|
"files": [
|
|
46
46
|
"/fallback/_utils.js",
|
|
47
47
|
"/fallback/base32.js",
|
|
48
|
+
"/fallback/base58check.js",
|
|
48
49
|
"/fallback/base64.js",
|
|
49
50
|
"/fallback/encoding.js",
|
|
50
51
|
"/fallback/encoding.labels.js",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"/base32.js",
|
|
66
67
|
"/base58.js",
|
|
67
68
|
"/base58check.js",
|
|
69
|
+
"/base58check.node.js",
|
|
68
70
|
"/base64.js",
|
|
69
71
|
"/base64.d.ts",
|
|
70
72
|
"/bech32.js",
|
|
@@ -92,7 +94,10 @@
|
|
|
92
94
|
},
|
|
93
95
|
"./base32.js": "./base32.js",
|
|
94
96
|
"./base58.js": "./base58.js",
|
|
95
|
-
"./base58check.js":
|
|
97
|
+
"./base58check.js": {
|
|
98
|
+
"node": "./base58check.node.js",
|
|
99
|
+
"default": "./base58check.js"
|
|
100
|
+
},
|
|
96
101
|
"./base64.js": {
|
|
97
102
|
"types": "./base64.d.ts",
|
|
98
103
|
"default": "./base64.js"
|
package/utf16.js
CHANGED
|
@@ -9,7 +9,7 @@ const decoderFatalBE = canDecoders ? new TextDecoder('utf-16be', { ignoreBOM, fa
|
|
|
9
9
|
const decoderLooseBE = canDecoders ? new TextDecoder('utf-16be', { ignoreBOM }) : null
|
|
10
10
|
const decoderFatal16 = isLE ? decoderFatalLE : decoderFatalBE
|
|
11
11
|
const decoderLoose16 = isLE ? decoderLooseLE : decoderFatalBE
|
|
12
|
-
const { isWellFormed } = String.prototype
|
|
12
|
+
const { isWellFormed, toWellFormed } = String.prototype
|
|
13
13
|
|
|
14
14
|
const { E_STRICT, E_STRICT_UNICODE } = js
|
|
15
15
|
|
|
@@ -61,8 +61,9 @@ function decode(input, loose = false, format = 'uint16') {
|
|
|
61
61
|
throw new TypeError('Unknown format')
|
|
62
62
|
}
|
|
63
63
|
|
|
64
|
-
const str = js.decode(u16, loose, !loose && isWellFormed)
|
|
64
|
+
const str = js.decode(u16, loose, (!loose && isWellFormed) || (loose && toWellFormed))
|
|
65
65
|
if (!loose && isWellFormed && !isWellFormed.call(str)) throw new TypeError(E_STRICT)
|
|
66
|
+
if (loose && toWellFormed) return toWellFormed.call(str)
|
|
66
67
|
|
|
67
68
|
return str
|
|
68
69
|
}
|
package/utf16.node.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { isDeno, isLE } from './fallback/_utils.js'
|
|
2
2
|
import { E_STRICT, E_STRICT_UNICODE } from './fallback/utf16.js'
|
|
3
3
|
|
|
4
4
|
if (Buffer.TYPED_ARRAY_SUPPORT) throw new Error('Unexpected Buffer polyfill')
|
|
5
5
|
|
|
6
|
-
const { isWellFormed } = String.prototype
|
|
6
|
+
const { isWellFormed, toWellFormed } = String.prototype
|
|
7
7
|
const to8 = (a) => new Uint8Array(a.buffer, a.byteOffset, a.byteLength)
|
|
8
8
|
|
|
9
9
|
// Unlike utf8, operates on Uint16Arrays by default
|
|
@@ -14,9 +14,10 @@ function encode(str, loose = false, format = 'uint16') {
|
|
|
14
14
|
throw new TypeError('Unknown format')
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
if (
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
if (loose) {
|
|
18
|
+
str = toWellFormed.call(str) // Buffer doesn't do this with utf16 encoding
|
|
19
|
+
} else if (!isWellFormed.call(str)) {
|
|
20
|
+
throw new TypeError(E_STRICT_UNICODE)
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
const ble = Buffer.from(str, 'utf-16le')
|
|
@@ -51,9 +52,9 @@ function decodeNode(input, loose = false, format = 'uint16') {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
const str = ble.ucs2Slice(0, ble.byteLength)
|
|
55
|
+
if (loose) return toWellFormed.call(str)
|
|
54
56
|
if (isWellFormed.call(str)) return str
|
|
55
|
-
|
|
56
|
-
return nativeDecoder.decode(Buffer.from(str)) // fixup (see above)
|
|
57
|
+
throw new TypeError(E_STRICT)
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
function decodeDecoder(input, loose = false, format = 'uint16') {
|