@exodus/bytes 1.0.0-rc.5 → 1.0.0-rc.7
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/base32.js +12 -5
- package/base58.js +4 -4
- package/base58check.js +49 -11
- package/base64.js +72 -40
- package/fallback/_utils.js +10 -1
- package/fallback/base32.js +71 -36
- package/fallback/base64.js +57 -27
- package/fallback/hex.js +137 -46
- package/fallback/latin1.js +113 -0
- package/fallback/utf8.js +77 -112
- package/hex.js +2 -16
- package/hex.node.js +26 -0
- package/package.json +22 -6
- package/utf8.js +10 -46
- package/utf8.node.js +33 -0
package/hex.js
CHANGED
|
@@ -2,30 +2,16 @@ import { assertUint8 } from './assert.js'
|
|
|
2
2
|
import { typedView } from './array.js'
|
|
3
3
|
import * as js from './fallback/hex.js'
|
|
4
4
|
|
|
5
|
-
const { Buffer } = globalThis // Buffer is optional, only used when native
|
|
6
|
-
const haveNativeBuffer = Buffer && !Buffer.TYPED_ARRAY_SUPPORT
|
|
7
5
|
const { toHex: webHex } = Uint8Array.prototype // Modern engines have this
|
|
8
6
|
|
|
9
|
-
const { E_HEX } = js
|
|
10
|
-
|
|
11
7
|
export function toHex(arr) {
|
|
12
8
|
assertUint8(arr)
|
|
13
9
|
if (arr.length === 0) return ''
|
|
14
10
|
if (webHex && arr.toHex === webHex) return arr.toHex()
|
|
15
|
-
|
|
16
|
-
if (arr.constructor === Buffer && Buffer.isBuffer(arr)) return arr.toString('hex')
|
|
17
|
-
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength).toString('hex')
|
|
11
|
+
return js.toHex(arr)
|
|
18
12
|
}
|
|
19
13
|
|
|
20
14
|
// Unlike Buffer.from(), throws on invalid input
|
|
21
15
|
export const fromHex = Uint8Array.fromHex
|
|
22
16
|
? (str, format = 'uint8') => typedView(Uint8Array.fromHex(str), format)
|
|
23
|
-
:
|
|
24
|
-
? (str, format = 'uint8') => {
|
|
25
|
-
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
26
|
-
if (str.length % 2 !== 0) throw new SyntaxError(E_HEX)
|
|
27
|
-
const buf = Buffer.from(str, 'hex') // will stop on first non-hex character, so we can just validate length
|
|
28
|
-
if (buf.length * 2 !== str.length) throw new SyntaxError(E_HEX)
|
|
29
|
-
return typedView(buf, format)
|
|
30
|
-
}
|
|
31
|
-
: (str, format = 'uint8') => typedView(js.fromHex(str), format)
|
|
17
|
+
: (str, format = 'uint8') => typedView(js.fromHex(str), format)
|
package/hex.node.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { assertUint8 } from './assert.js'
|
|
2
|
+
import { typedView } from './array.js'
|
|
3
|
+
import { E_HEX } from './fallback/hex.js'
|
|
4
|
+
|
|
5
|
+
if (Buffer.TYPED_ARRAY_SUPPORT) throw new Error('Unexpected Buffer polyfill')
|
|
6
|
+
|
|
7
|
+
const { toHex: webHex } = Uint8Array.prototype // Modern engines have this
|
|
8
|
+
|
|
9
|
+
export function toHex(arr) {
|
|
10
|
+
assertUint8(arr)
|
|
11
|
+
if (arr.length === 0) return ''
|
|
12
|
+
if (webHex && arr.toHex === webHex) return arr.toHex()
|
|
13
|
+
if (arr.constructor === Buffer && Buffer.isBuffer(arr)) return arr.hexSlice(0, arr.byteLength)
|
|
14
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength).hexSlice(0, arr.byteLength)
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Unlike Buffer.from(), throws on invalid input
|
|
18
|
+
export const fromHex = Uint8Array.fromHex
|
|
19
|
+
? (str, format = 'uint8') => typedView(Uint8Array.fromHex(str), format)
|
|
20
|
+
: (str, format = 'uint8') => {
|
|
21
|
+
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
22
|
+
if (str.length % 2 !== 0) throw new SyntaxError(E_HEX)
|
|
23
|
+
const buf = Buffer.from(str, 'hex') // will stop on first non-hex character, so we can just validate length
|
|
24
|
+
if (buf.length * 2 !== str.length) throw new SyntaxError(E_HEX)
|
|
25
|
+
return typedView(buf, format)
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bytes",
|
|
3
|
-
"version": "1.0.0-rc.
|
|
3
|
+
"version": "1.0.0-rc.7",
|
|
4
4
|
"description": "Various operations on Uint8Array data",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"lint": "eslint .",
|
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"type": "module",
|
|
40
40
|
"files": [
|
|
41
41
|
"/fallback/_utils.js",
|
|
42
|
+
"/fallback/latin1.js",
|
|
42
43
|
"/fallback/base32.js",
|
|
43
44
|
"/fallback/base64.js",
|
|
44
45
|
"/fallback/hex.js",
|
|
@@ -49,7 +50,9 @@
|
|
|
49
50
|
"/base58.js",
|
|
50
51
|
"/base58check.js",
|
|
51
52
|
"/base64.js",
|
|
53
|
+
"/hex.node.js",
|
|
52
54
|
"/hex.js",
|
|
55
|
+
"/utf8.node.js",
|
|
53
56
|
"/utf8.js"
|
|
54
57
|
],
|
|
55
58
|
"exports": {
|
|
@@ -58,8 +61,18 @@
|
|
|
58
61
|
"./base58.js": "./base58.js",
|
|
59
62
|
"./base58check.js": "./base58check.js",
|
|
60
63
|
"./base64.js": "./base64.js",
|
|
61
|
-
"./hex.js":
|
|
62
|
-
|
|
64
|
+
"./hex.js": {
|
|
65
|
+
"node": "./hex.node.js",
|
|
66
|
+
"default": "./hex.js"
|
|
67
|
+
},
|
|
68
|
+
"./utf16.js": {
|
|
69
|
+
"node": "./utf16.node.js",
|
|
70
|
+
"default": "./utf16.js"
|
|
71
|
+
},
|
|
72
|
+
"./utf8.js": {
|
|
73
|
+
"node": "./utf8.node.js",
|
|
74
|
+
"default": "./utf8.js"
|
|
75
|
+
}
|
|
63
76
|
},
|
|
64
77
|
"peerDependencies": {
|
|
65
78
|
"@exodus/crypto": "^1.0.0-rc.4"
|
|
@@ -74,12 +87,12 @@
|
|
|
74
87
|
"@exodus/crypto": "1.0.0-rc.29",
|
|
75
88
|
"@exodus/eslint-config": "^5.24.0",
|
|
76
89
|
"@exodus/prettier": "^1.0.0",
|
|
77
|
-
"@exodus/test": "^1.0.0-rc.
|
|
90
|
+
"@exodus/test": "^1.0.0-rc.108",
|
|
78
91
|
"@noble/hashes": "^2.0.1",
|
|
79
92
|
"@scure/base": "^1.2.6",
|
|
80
93
|
"@stablelib/base64": "^2.0.1",
|
|
81
94
|
"@stablelib/hex": "^2.0.1",
|
|
82
|
-
"@types/node": "^
|
|
95
|
+
"@types/node": "^22.13.0",
|
|
83
96
|
"base-x": "^5.0.1",
|
|
84
97
|
"base32.js": "^0.1.0",
|
|
85
98
|
"base64-js": "^1.5.1",
|
|
@@ -91,9 +104,12 @@
|
|
|
91
104
|
"eslint": "^8.44.0",
|
|
92
105
|
"fast-base64-decode": "^2.0.0",
|
|
93
106
|
"fast-base64-encode": "^1.0.0",
|
|
107
|
+
"hextreme": "^1.0.7",
|
|
94
108
|
"hi-base32": "^0.5.1",
|
|
109
|
+
"iconv-lite": "^0.7.0",
|
|
95
110
|
"jsvu": "^3.0.0",
|
|
96
|
-
"text-encoding": "^0.7.0"
|
|
111
|
+
"text-encoding": "^0.7.0",
|
|
112
|
+
"typescript": "^5.9.3"
|
|
97
113
|
},
|
|
98
114
|
"prettier": "@exodus/prettier",
|
|
99
115
|
"packageManager": "pnpm@10.12.1+sha256.889bac470ec93ccc3764488a19d6ba8f9c648ad5e50a9a6e4be3768a5de387a3"
|
package/utf8.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { assertUint8 } from './assert.js'
|
|
2
2
|
import { typedView } from './array.js'
|
|
3
3
|
import * as js from './fallback/utf8.js'
|
|
4
|
+
import { asciiPrefix, decodeLatin1 } from './fallback/latin1.js'
|
|
4
5
|
|
|
5
6
|
const { Buffer, TextEncoder, TextDecoder, decodeURIComponent, escape } = globalThis // Buffer is optional
|
|
6
7
|
const haveNativeBuffer = Buffer && !Buffer.TYPED_ARRAY_SUPPORT
|
|
@@ -18,7 +19,7 @@ const { E_STRICT, E_STRICT_UNICODE } = js
|
|
|
18
19
|
const shouldUseEscapePath = Boolean(globalThis.HermesInternal) // faster only on Hermes, js path beats it on normal engines
|
|
19
20
|
|
|
20
21
|
function deLoose(str, loose, res) {
|
|
21
|
-
if (loose) return res
|
|
22
|
+
if (loose || str.length === res.length) return res // length is equal only for ascii, which is automatically fine
|
|
22
23
|
if (isWellFormed) {
|
|
23
24
|
// We have a fast native method
|
|
24
25
|
if (isWellFormed.call(str)) return res
|
|
@@ -51,64 +52,27 @@ function encode(str, loose = false) {
|
|
|
51
52
|
return js.encode(str, loose)
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
let escapes
|
|
55
|
-
|
|
56
|
-
function toEscapesPart(arr, start, end) {
|
|
57
|
-
let o = ''
|
|
58
|
-
let i = start
|
|
59
|
-
const last3 = end - 3
|
|
60
|
-
// Unrolled loop is faster
|
|
61
|
-
while (i < last3) {
|
|
62
|
-
const a = arr[i++]
|
|
63
|
-
const b = arr[i++]
|
|
64
|
-
const c = arr[i++]
|
|
65
|
-
const d = arr[i++]
|
|
66
|
-
o += escapes[a]
|
|
67
|
-
o += escapes[b]
|
|
68
|
-
o += escapes[c]
|
|
69
|
-
o += escapes[d]
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
while (i < end) o += escapes[arr[i++]]
|
|
73
|
-
return o
|
|
74
|
-
}
|
|
75
|
-
|
|
76
55
|
function decode(arr, loose = false) {
|
|
77
56
|
assertUint8(arr)
|
|
78
57
|
if (haveDecoder) return loose ? decoderLoose.decode(arr) : decoderFatal.decode(arr) // Node.js and browsers
|
|
79
58
|
// No reason to use native Buffer: it's not faster than TextDecoder, needs rechecks in non-loose mode, and Node.js has TextDecoder
|
|
80
59
|
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
const length = arr.length
|
|
85
|
-
let o
|
|
86
|
-
if (length > 30_000) {
|
|
87
|
-
// Limit concatenation to avoid excessive GC
|
|
88
|
-
// TODO: recheck thresholds on Hermes (taken from hex)
|
|
89
|
-
const concat = []
|
|
90
|
-
for (let i = 0; i < length; ) {
|
|
91
|
-
const step = i + 500
|
|
92
|
-
const end = step > length ? length : step
|
|
93
|
-
concat.push(toEscapesPart(arr, i, end))
|
|
94
|
-
i = end
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
o = concat.join('')
|
|
98
|
-
concat.length = 0
|
|
99
|
-
} else {
|
|
100
|
-
o = toEscapesPart(arr, 0, length)
|
|
101
|
-
}
|
|
60
|
+
// Fast path for ASCII prefix, this is faster than all alternatives below
|
|
61
|
+
const prefix = decodeLatin1(arr, 0, asciiPrefix(arr))
|
|
62
|
+
if (prefix.length === arr.length) return prefix
|
|
102
63
|
|
|
64
|
+
// This codepath gives a ~3x perf boost on Hermes
|
|
65
|
+
if (shouldUseEscapePath && escape && decodeURIComponent) {
|
|
66
|
+
const o = escape(decodeLatin1(arr, prefix.length, arr.length))
|
|
103
67
|
try {
|
|
104
|
-
return decodeURIComponent(o) //
|
|
68
|
+
return prefix + decodeURIComponent(o) // Latin1 to utf8
|
|
105
69
|
} catch {
|
|
106
70
|
if (!loose) throw new TypeError(E_STRICT)
|
|
107
71
|
// Ok, we have to use manual implementation for loose decoder
|
|
108
72
|
}
|
|
109
73
|
}
|
|
110
74
|
|
|
111
|
-
return js.decode(arr, loose)
|
|
75
|
+
return prefix + js.decode(arr, loose, prefix.length)
|
|
112
76
|
}
|
|
113
77
|
|
|
114
78
|
export const utf8fromString = (str, format = 'uint8') => typedView(encode(str, false), format)
|
package/utf8.node.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { assertUint8 } from './assert.js'
|
|
2
|
+
import { typedView } from './array.js'
|
|
3
|
+
import { E_STRICT_UNICODE } from './fallback/utf8.js'
|
|
4
|
+
import { isAscii } from 'node:buffer'
|
|
5
|
+
|
|
6
|
+
if (Buffer.TYPED_ARRAY_SUPPORT) throw new Error('Unexpected Buffer polyfill')
|
|
7
|
+
|
|
8
|
+
const decoderFatal = new TextDecoder('utf8', { ignoreBOM: true, fatal: true })
|
|
9
|
+
const decoderLoose = new TextDecoder('utf8', { ignoreBOM: true })
|
|
10
|
+
const { isWellFormed } = String.prototype
|
|
11
|
+
|
|
12
|
+
function encode(str, loose = false) {
|
|
13
|
+
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
14
|
+
const res = Buffer.from(str)
|
|
15
|
+
if (loose || str.length === res.length || isWellFormed.call(str)) return res // length is equal only for ascii, which is automatically fine
|
|
16
|
+
throw new TypeError(E_STRICT_UNICODE)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function decode(arr, loose = false) {
|
|
20
|
+
assertUint8(arr)
|
|
21
|
+
if (isAscii(arr)) {
|
|
22
|
+
// On non-ascii strings, this loses ~10% * [relative position of the first non-ascii byte] (up to 10% total)
|
|
23
|
+
// On ascii strings, this wins 1.5x on loose = false and 1.3x on loose = true
|
|
24
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength).latin1Slice(0, arr.byteLength) // .latin1Slice is faster than .asciiSlice
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return loose ? decoderLoose.decode(arr) : decoderFatal.decode(arr)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export const utf8fromString = (str, format = 'uint8') => typedView(encode(str, false), format)
|
|
31
|
+
export const utf8fromStringLoose = (str, format = 'uint8') => typedView(encode(str, true), format)
|
|
32
|
+
export const utf8toString = (arr) => decode(arr, false)
|
|
33
|
+
export const utf8toStringLoose = (arr) => decode(arr, true)
|