@exodus/bytes 1.0.0-rc.2 → 1.0.0-rc.3
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/array.js +1 -1
- package/assert.js +2 -1
- package/base64.js +7 -108
- package/fallback/base64.js +127 -0
- package/fallback/hex.js +90 -0
- package/hex.js +10 -66
- package/package.json +5 -3
package/array.js
CHANGED
package/assert.js
CHANGED
|
@@ -12,7 +12,8 @@ const makeMessage = (name, extra) => `Expected${name ? ` ${name} to be` : ''} an
|
|
|
12
12
|
const TypedArray = Object.getPrototypeOf(Uint8Array)
|
|
13
13
|
|
|
14
14
|
export function assertTypedArray(arr) {
|
|
15
|
-
|
|
15
|
+
if (arr instanceof TypedArray) return
|
|
16
|
+
throw new TypeError('Expected a TypedArray instance')
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
export function assertUint8(arr, { name, length, ...rest } = {}) {
|
package/base64.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assert, assertUint8 } from './assert.js'
|
|
2
|
-
import {
|
|
2
|
+
import { typedView } from './array.js'
|
|
3
|
+
import * as js from './fallback/base64.js'
|
|
3
4
|
|
|
4
5
|
// See https://datatracker.ietf.org/doc/html/rfc4648
|
|
5
6
|
|
|
@@ -13,7 +14,7 @@ const { toBase64: web64 } = Uint8Array.prototype // Modern engines have this
|
|
|
13
14
|
export function toBase64(x) {
|
|
14
15
|
assertUint8(x)
|
|
15
16
|
if (web64 && x.toBase64 === web64) return x.toBase64() // Modern
|
|
16
|
-
if (!haveNativeBuffer) return
|
|
17
|
+
if (!haveNativeBuffer) return js.toBase64(x, false, true) // Fallback
|
|
17
18
|
if (x.constructor === Buffer && Buffer.isBuffer(x)) return x.toString('base64') // Older Node.js
|
|
18
19
|
return Buffer.from(x.buffer, x.byteOffset, x.byteLength).toString('base64') // Older Node.js
|
|
19
20
|
}
|
|
@@ -22,7 +23,7 @@ export function toBase64(x) {
|
|
|
22
23
|
export function toBase64url(x) {
|
|
23
24
|
assertUint8(x)
|
|
24
25
|
if (web64 && x.toBase64 === web64) return x.toBase64({ alphabet: 'base64url', omitPadding: true }) // Modern
|
|
25
|
-
if (!haveNativeBuffer) return
|
|
26
|
+
if (!haveNativeBuffer) return js.toBase64(x, true, false) // Fallback
|
|
26
27
|
if (x.constructor === Buffer && Buffer.isBuffer(x)) return x.toString('base64url') // Older Node.js
|
|
27
28
|
return Buffer.from(x.buffer, x.byteOffset, x.byteLength).toString('base64url') // Older Node.js
|
|
28
29
|
}
|
|
@@ -42,7 +43,7 @@ export function fromBase64(str, format = 'uint8') {
|
|
|
42
43
|
assert(str[str.length - 3] !== '=', 'Excessive padding') // no more than two = at the end
|
|
43
44
|
}
|
|
44
45
|
|
|
45
|
-
return
|
|
46
|
+
return typedView(fromBase64common(str, false), format)
|
|
46
47
|
}
|
|
47
48
|
|
|
48
49
|
// Accepts both only non-padded strict base64url
|
|
@@ -53,7 +54,7 @@ export function fromBase64url(str, format = 'uint8') {
|
|
|
53
54
|
assert(str.length % 4 !== 1, 'Invalid base64 length') // JSC misses this in fromBase64
|
|
54
55
|
assert(!str.endsWith('='), 'Did not expect padding in base64url input') // inclusion is checked separately
|
|
55
56
|
|
|
56
|
-
return
|
|
57
|
+
return typedView(fromBase64common(str, true), format)
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
let fromBase64common
|
|
@@ -87,7 +88,7 @@ if (Uint8Array.fromBase64) {
|
|
|
87
88
|
if (at >= 0) assert(!/[^=]/iu.test(str.slice(at)), 'Invalid padding')
|
|
88
89
|
}
|
|
89
90
|
|
|
90
|
-
arr = haveNativeBuffer ? Buffer.from(str, 'base64') :
|
|
91
|
+
arr = haveNativeBuffer ? Buffer.from(str, 'base64') : js.fromBase64(str)
|
|
91
92
|
}
|
|
92
93
|
|
|
93
94
|
if (arr.length % 3 !== 0) {
|
|
@@ -101,105 +102,3 @@ if (Uint8Array.fromBase64) {
|
|
|
101
102
|
return arr
|
|
102
103
|
}
|
|
103
104
|
}
|
|
104
|
-
|
|
105
|
-
const BASE64 = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/']
|
|
106
|
-
const BASE64URL = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_']
|
|
107
|
-
|
|
108
|
-
const BASE64_PAIRS = []
|
|
109
|
-
const BASE64URL_PAIRS = []
|
|
110
|
-
|
|
111
|
-
// We construct output by concatenating chars, this seems to be fine enough on modern JS engines
|
|
112
|
-
function toBase64js(arr, alphabet, padding) {
|
|
113
|
-
assertUint8(arr)
|
|
114
|
-
const fullChunks = Math.floor(arr.length / 3)
|
|
115
|
-
const fullChunksBytes = fullChunks * 3
|
|
116
|
-
let o = ''
|
|
117
|
-
let i = 0
|
|
118
|
-
|
|
119
|
-
const pairs = alphabet === BASE64URL ? BASE64URL_PAIRS : BASE64_PAIRS
|
|
120
|
-
if (pairs.length === 0) {
|
|
121
|
-
for (let i = 0; i < 64; i++) {
|
|
122
|
-
for (let j = 0; j < 64; j++) pairs.push(`${alphabet[i]}${alphabet[j]}`)
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
// Fast path for complete blocks
|
|
127
|
-
// This whole loop can be commented out, the algorithm won't change, it's just an optimization of the next loop
|
|
128
|
-
for (; i < fullChunksBytes; i += 3) {
|
|
129
|
-
const a = arr[i]
|
|
130
|
-
const b = arr[i + 1]
|
|
131
|
-
const c = arr[i + 2]
|
|
132
|
-
o += pairs[(a << 4) | (b >> 4)] + pairs[((b & 0x0f) << 8) | c]
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
// If we have something left, process it with a full algo
|
|
136
|
-
let carry = 0
|
|
137
|
-
let shift = 2 // First byte needs to be shifted by 2 to get 6 bits
|
|
138
|
-
const length = arr.length
|
|
139
|
-
for (; i < length; i++) {
|
|
140
|
-
const x = arr[i]
|
|
141
|
-
o += alphabet[carry | (x >> shift)] // shift >= 2, so this fits
|
|
142
|
-
if (shift === 6) {
|
|
143
|
-
shift = 0
|
|
144
|
-
o += alphabet[x & 0x3f]
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
carry = (x << (6 - shift)) & 0x3f
|
|
148
|
-
shift += 2 // Each byte prints 6 bits and leaves 2 bits
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
if (shift !== 2) o += alphabet[carry] // shift 2 means we have no carry left
|
|
152
|
-
if (padding) o += ['', '==', '='][length - fullChunksBytes]
|
|
153
|
-
|
|
154
|
-
return o
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
// Assumes no chars after =, checked
|
|
158
|
-
let fromBase64jsMap
|
|
159
|
-
|
|
160
|
-
function fromBase64js(str) {
|
|
161
|
-
const map = fromBase64jsMap || new Array(256)
|
|
162
|
-
if (!fromBase64jsMap) {
|
|
163
|
-
fromBase64jsMap = map
|
|
164
|
-
BASE64.forEach((c, i) => (map[c.charCodeAt(0)] = i))
|
|
165
|
-
map['-'.charCodeAt(0)] = map['+'.charCodeAt(0)] // for base64url
|
|
166
|
-
map['_'.charCodeAt(0)] = map['/'.charCodeAt(0)] // for base64url
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
let inputLength = str.length
|
|
170
|
-
while (str[inputLength - 1] === '=') inputLength--
|
|
171
|
-
|
|
172
|
-
const arr = new Uint8Array(Math.floor((inputLength * 3) / 4))
|
|
173
|
-
const tailLength = inputLength % 4
|
|
174
|
-
const mainLength = inputLength - tailLength // multiples of 4
|
|
175
|
-
|
|
176
|
-
let at = 0
|
|
177
|
-
let i = 0
|
|
178
|
-
let tmp
|
|
179
|
-
|
|
180
|
-
while (i < mainLength) {
|
|
181
|
-
tmp =
|
|
182
|
-
(map[str.charCodeAt(i)] << 18) |
|
|
183
|
-
(map[str.charCodeAt(i + 1)] << 12) |
|
|
184
|
-
(map[str.charCodeAt(i + 2)] << 6) |
|
|
185
|
-
map[str.charCodeAt(i + 3)]
|
|
186
|
-
arr[at++] = tmp >> 16
|
|
187
|
-
arr[at++] = (tmp >> 8) & 0xff
|
|
188
|
-
arr[at++] = tmp & 0xff
|
|
189
|
-
i += 4
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (tailLength === 3) {
|
|
193
|
-
tmp =
|
|
194
|
-
(map[str.charCodeAt(i)] << 10) |
|
|
195
|
-
(map[str.charCodeAt(i + 1)] << 4) |
|
|
196
|
-
(map[str.charCodeAt(i + 2)] >> 2)
|
|
197
|
-
arr[at++] = (tmp >> 8) & 0xff
|
|
198
|
-
arr[at++] = tmp & 0xff
|
|
199
|
-
} else if (tailLength === 2) {
|
|
200
|
-
tmp = (map[str.charCodeAt(i)] << 2) | (map[str.charCodeAt(i + 1)] >> 4)
|
|
201
|
-
arr[at++] = tmp & 0xff
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return arr
|
|
205
|
-
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { assertUint8 } from '../assert.js'
|
|
2
|
+
|
|
3
|
+
// See https://datatracker.ietf.org/doc/html/rfc4648
|
|
4
|
+
|
|
5
|
+
const { TextDecoder } = globalThis
|
|
6
|
+
const nativeDecoder = TextDecoder?.toString().includes('[native code]') ? new TextDecoder() : null
|
|
7
|
+
const BASE64 = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/']
|
|
8
|
+
const BASE64URL = [...'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_']
|
|
9
|
+
const BASE64_PAIRS = []
|
|
10
|
+
const BASE64URL_PAIRS = []
|
|
11
|
+
const BASE64_CODES = nativeDecoder ? new Uint8Array(64) : null
|
|
12
|
+
const BASE64URL_CODES = nativeDecoder ? new Uint8Array(64) : null
|
|
13
|
+
|
|
14
|
+
// Alternatively, we could have mapped 0-255 bytes to charcodes and just used btoa(ascii),
|
|
15
|
+
// but that approach is _slower_ than our toBase64js function, even on Hermes
|
|
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 = Math.floor(arr.length / 3)
|
|
21
|
+
const fullChunksBytes = fullChunks * 3
|
|
22
|
+
let o = ''
|
|
23
|
+
let i = 0
|
|
24
|
+
|
|
25
|
+
const alphabet = isURL ? BASE64URL : BASE64
|
|
26
|
+
const pairs = isURL ? BASE64URL_PAIRS : BASE64_PAIRS
|
|
27
|
+
const map = isURL ? BASE64_CODES : BASE64URL_CODES
|
|
28
|
+
if (pairs.length === 0) {
|
|
29
|
+
for (let i = 0; i < 64; i++) {
|
|
30
|
+
for (let j = 0; j < 64; j++) pairs.push(`${alphabet[i]}${alphabet[j]}`)
|
|
31
|
+
if (map) map[i] = alphabet[i].charCodeAt(0)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Fast path for complete blocks
|
|
36
|
+
// This whole loop can be commented out, the algorithm won't change, it's just an optimization of the next loop
|
|
37
|
+
if (nativeDecoder) {
|
|
38
|
+
const oa = new Uint8Array(fullChunks * 4)
|
|
39
|
+
for (let j = 0; i < fullChunksBytes; i += 3) {
|
|
40
|
+
const a = arr[i]
|
|
41
|
+
const b = arr[i + 1]
|
|
42
|
+
const c = arr[i + 2]
|
|
43
|
+
oa[j++] = map[a >> 2]
|
|
44
|
+
oa[j++] = map[((a & 0x3) << 4) | (b >> 4)]
|
|
45
|
+
oa[j++] = map[((b & 0xf) << 2) | (c >> 6)]
|
|
46
|
+
oa[j++] = map[c & 0x3f]
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
o = nativeDecoder.decode(oa)
|
|
50
|
+
} else {
|
|
51
|
+
for (; i < fullChunksBytes; i += 3) {
|
|
52
|
+
const a = arr[i]
|
|
53
|
+
const b = arr[i + 1]
|
|
54
|
+
const c = arr[i + 2]
|
|
55
|
+
o += pairs[(a << 4) | (b >> 4)] + pairs[((b & 0x0f) << 8) | c]
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// If we have something left, process it with a full algo
|
|
60
|
+
let carry = 0
|
|
61
|
+
let shift = 2 // First byte needs to be shifted by 2 to get 6 bits
|
|
62
|
+
const length = arr.length
|
|
63
|
+
for (; i < length; i++) {
|
|
64
|
+
const x = arr[i]
|
|
65
|
+
o += alphabet[carry | (x >> shift)] // shift >= 2, so this fits
|
|
66
|
+
if (shift === 6) {
|
|
67
|
+
shift = 0
|
|
68
|
+
o += alphabet[x & 0x3f]
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
carry = (x << (6 - shift)) & 0x3f
|
|
72
|
+
shift += 2 // Each byte prints 6 bits and leaves 2 bits
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (shift !== 2) o += alphabet[carry] // shift 2 means we have no carry left
|
|
76
|
+
if (padding) o += ['', '==', '='][length - fullChunksBytes]
|
|
77
|
+
|
|
78
|
+
return o
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let fromBase64jsMap
|
|
82
|
+
|
|
83
|
+
// Assumes valid input and no chars after =, checked at API
|
|
84
|
+
// Last chunk is rechecked at API too
|
|
85
|
+
export function fromBase64(str) {
|
|
86
|
+
const map = fromBase64jsMap || new Array(256)
|
|
87
|
+
if (!fromBase64jsMap) {
|
|
88
|
+
fromBase64jsMap = map
|
|
89
|
+
BASE64.forEach((c, i) => (map[c.charCodeAt(0)] = i))
|
|
90
|
+
map['-'.charCodeAt(0)] = map['+'.charCodeAt(0)] // for base64url
|
|
91
|
+
map['_'.charCodeAt(0)] = map['/'.charCodeAt(0)] // for base64url
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
let inputLength = str.length
|
|
95
|
+
while (str[inputLength - 1] === '=') inputLength--
|
|
96
|
+
|
|
97
|
+
const arr = new Uint8Array(Math.floor((inputLength * 3) / 4))
|
|
98
|
+
const tailLength = inputLength % 4
|
|
99
|
+
const mainLength = inputLength - tailLength // multiples of 4
|
|
100
|
+
|
|
101
|
+
let at = 0
|
|
102
|
+
let i = 0
|
|
103
|
+
let tmp
|
|
104
|
+
|
|
105
|
+
while (i < mainLength) {
|
|
106
|
+
// a [ b c ] d, each 6 bits
|
|
107
|
+
const bc = (map[str.charCodeAt(i + 1)] << 6) | map[str.charCodeAt(i + 2)]
|
|
108
|
+
arr[at++] = (map[str.charCodeAt(i)] << 2) | (bc >> 10)
|
|
109
|
+
arr[at++] = (bc >> 2) & 0xff
|
|
110
|
+
arr[at++] = ((bc << 6) & 0xff) | map[str.charCodeAt(i + 3)]
|
|
111
|
+
i += 4
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (tailLength === 3) {
|
|
115
|
+
tmp =
|
|
116
|
+
(map[str.charCodeAt(i)] << 10) |
|
|
117
|
+
(map[str.charCodeAt(i + 1)] << 4) |
|
|
118
|
+
(map[str.charCodeAt(i + 2)] >> 2)
|
|
119
|
+
arr[at++] = (tmp >> 8) & 0xff
|
|
120
|
+
arr[at++] = tmp & 0xff
|
|
121
|
+
} else if (tailLength === 2) {
|
|
122
|
+
tmp = (map[str.charCodeAt(i)] << 2) | (map[str.charCodeAt(i + 1)] >> 4)
|
|
123
|
+
arr[at++] = tmp & 0xff
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return arr
|
|
127
|
+
}
|
package/fallback/hex.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { assert, assertUint8 } from '../assert.js'
|
|
2
|
+
|
|
3
|
+
// We use TextEncoder here to parse strings to charcodes, this is faster than individual charCodeAt calls
|
|
4
|
+
const { TextEncoder } = globalThis // Buffer is optional, only used when native
|
|
5
|
+
const nativeEncoder = TextEncoder?.toString().includes('[native code]') ? new TextEncoder() : null
|
|
6
|
+
|
|
7
|
+
let hexArray
|
|
8
|
+
let dehexArray
|
|
9
|
+
|
|
10
|
+
function toHexPart(arr, start, end) {
|
|
11
|
+
let o = ''
|
|
12
|
+
let i = start
|
|
13
|
+
const last3 = end - 3
|
|
14
|
+
// Unrolled loop is faster
|
|
15
|
+
while (i < last3) {
|
|
16
|
+
const a = arr[i++]
|
|
17
|
+
const b = arr[i++]
|
|
18
|
+
const c = arr[i++]
|
|
19
|
+
const d = arr[i++]
|
|
20
|
+
o += hexArray[a]
|
|
21
|
+
o += hexArray[b]
|
|
22
|
+
o += hexArray[c]
|
|
23
|
+
o += hexArray[d]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
while (i < end) o += hexArray[arr[i++]]
|
|
27
|
+
return o
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function toHex(arr) {
|
|
31
|
+
assertUint8(arr)
|
|
32
|
+
|
|
33
|
+
if (!hexArray) hexArray = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'))
|
|
34
|
+
const length = arr.length // this helps Hermes
|
|
35
|
+
|
|
36
|
+
if (length > 30_000) {
|
|
37
|
+
// Limit concatenation to avoid excessive GC
|
|
38
|
+
// Thresholds checked on Hermes
|
|
39
|
+
const concat = []
|
|
40
|
+
for (let i = 0; i < length; ) {
|
|
41
|
+
const step = i + 500
|
|
42
|
+
const end = step > length ? length : step
|
|
43
|
+
concat.push(toHexPart(arr, i, end))
|
|
44
|
+
i = end
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const res = concat.join('')
|
|
48
|
+
concat.length = 0
|
|
49
|
+
return res
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return toHexPart(arr, 0, length)
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function fromHex(str) {
|
|
56
|
+
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
57
|
+
assert(str.length % 2 === 0, 'Input is not a hex string')
|
|
58
|
+
|
|
59
|
+
// We don't use native Buffer impl, as rechecking input make it slower than pure js
|
|
60
|
+
// This path is used only on older engines though
|
|
61
|
+
|
|
62
|
+
if (!dehexArray) {
|
|
63
|
+
dehexArray = new Array(103) // f is 102
|
|
64
|
+
for (let i = 0; i < 16; i++) {
|
|
65
|
+
const s = i.toString(16)
|
|
66
|
+
dehexArray[s.charCodeAt(0)] = dehexArray[s.toUpperCase().charCodeAt(0)] = i
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const length = str.length / 2 // this helps Hermes in loops
|
|
71
|
+
const arr = new Uint8Array(length)
|
|
72
|
+
let j = 0
|
|
73
|
+
if (nativeEncoder) {
|
|
74
|
+
// Native encoder path is beneficial even for small arrays in Hermes
|
|
75
|
+
const codes = nativeEncoder.encode(str)
|
|
76
|
+
for (let i = 0; i < length; i++) {
|
|
77
|
+
const a = dehexArray[codes[j++]] * 16 + dehexArray[codes[j++]]
|
|
78
|
+
if (!a && a !== 0) throw new Error('Input is not a hex string')
|
|
79
|
+
arr[i] = a
|
|
80
|
+
}
|
|
81
|
+
} else {
|
|
82
|
+
for (let i = 0; i < length; i++) {
|
|
83
|
+
const a = dehexArray[str.charCodeAt(j++)] * 16 + dehexArray[str.charCodeAt(j++)]
|
|
84
|
+
if (!a && a !== 0) throw new Error('Input is not a hex string')
|
|
85
|
+
arr[i] = a
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return arr
|
|
90
|
+
}
|
package/hex.js
CHANGED
|
@@ -1,78 +1,22 @@
|
|
|
1
|
-
import { assertTypedArray
|
|
2
|
-
import {
|
|
1
|
+
import { assertTypedArray } from './assert.js'
|
|
2
|
+
import { typedView } from './array.js'
|
|
3
|
+
import * as js from './fallback/hex.js'
|
|
3
4
|
|
|
4
5
|
const { Buffer } = globalThis // Buffer is optional, only used when native
|
|
5
6
|
const haveNativeBuffer = Buffer && !Buffer.TYPED_ARRAY_SUPPORT
|
|
6
7
|
const { toHex: webHex } = Uint8Array.prototype // Modern engines have this
|
|
7
8
|
|
|
8
|
-
let hexArray
|
|
9
|
-
let dehexArray
|
|
10
|
-
|
|
11
9
|
export function toHex(arr) {
|
|
12
10
|
assertTypedArray(arr)
|
|
13
11
|
if (!(arr instanceof Uint8Array)) arr = new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength)
|
|
12
|
+
if (arr.length === 0) return ''
|
|
14
13
|
if (webHex && arr.toHex === webHex) return arr.toHex()
|
|
15
|
-
if (haveNativeBuffer)
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (!hexArray) hexArray = Array.from({ length: 256 }, (_, i) => i.toString(16).padStart(2, '0'))
|
|
21
|
-
const length = arr.length // this helps Hermes
|
|
22
|
-
|
|
23
|
-
if (length > 30000) {
|
|
24
|
-
// Limit concatenation to avoid excessive GC
|
|
25
|
-
// Thresholds checked on Hermes
|
|
26
|
-
const concat = []
|
|
27
|
-
for (let i = 0; i < length; ) {
|
|
28
|
-
const step = i + 500
|
|
29
|
-
const end = step > length ? length : step
|
|
30
|
-
let chunk = ''
|
|
31
|
-
for (; i < end; i++) chunk += hexArray[arr[i]]
|
|
32
|
-
concat.push(chunk)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const res = concat.join('')
|
|
36
|
-
concat.length = 0
|
|
37
|
-
return res
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
let out = ''
|
|
41
|
-
for (let i = 0; i < length; i++) out += hexArray[arr[i]]
|
|
42
|
-
return out
|
|
14
|
+
if (!haveNativeBuffer) return js.toHex(arr)
|
|
15
|
+
if (arr.constructor === Buffer && Buffer.isBuffer(arr)) return arr.toString('hex')
|
|
16
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength).toString('hex')
|
|
43
17
|
}
|
|
44
18
|
|
|
45
19
|
// Unlike Buffer.from(), throws on invalid input
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} else {
|
|
50
|
-
fromHex = (str, format = 'uint8') => {
|
|
51
|
-
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
52
|
-
assert(str.length % 2 === 0, 'Input is not a hex string')
|
|
53
|
-
|
|
54
|
-
// We don't use native Buffer impl, as rechecking input make it slower than pure js
|
|
55
|
-
// This path is used only on older engines though
|
|
56
|
-
|
|
57
|
-
if (!dehexArray) {
|
|
58
|
-
dehexArray = new Array(103) // f is 102
|
|
59
|
-
for (let i = 0; i < 16; i++) {
|
|
60
|
-
const s = i.toString(16)
|
|
61
|
-
dehexArray[s.charCodeAt(0)] = dehexArray[s.toUpperCase().charCodeAt(0)] = i
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const arr = new Uint8Array(str.length / 2)
|
|
66
|
-
let j = 0
|
|
67
|
-
const length = arr.length // this helps Hermes
|
|
68
|
-
for (let i = 0; i < length; i++) {
|
|
69
|
-
const a = dehexArray[str.charCodeAt(j++)] * 16 + dehexArray[str.charCodeAt(j++)]
|
|
70
|
-
if (!a && Number.isNaN(a)) throw new Error('Input is not a hex string')
|
|
71
|
-
arr[i] = a
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
return fromTypedArray(arr, format)
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export { fromHex }
|
|
20
|
+
export const fromHex = Uint8Array.fromHex
|
|
21
|
+
? (str, format = 'uint8') => typedView(Uint8Array.fromHex(str), format)
|
|
22
|
+
: (str, format = 'uint8') => typedView(js.fromHex(str), format)
|
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.3",
|
|
4
4
|
"description": "Various operations on Uint8Array data",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"lint": "eslint .",
|
|
@@ -39,14 +39,16 @@
|
|
|
39
39
|
},
|
|
40
40
|
"type": "module",
|
|
41
41
|
"files": [
|
|
42
|
+
"/fallback/base64.js",
|
|
43
|
+
"/fallback/hex.js",
|
|
44
|
+
"/array.js",
|
|
42
45
|
"/assert.js",
|
|
43
46
|
"/base64.js",
|
|
44
|
-
"/array.js",
|
|
45
47
|
"/hex.js"
|
|
46
48
|
],
|
|
47
49
|
"exports": {
|
|
48
|
-
"./base64.js": "./base64.js",
|
|
49
50
|
"./array.js": "./array.js",
|
|
51
|
+
"./base64.js": "./base64.js",
|
|
50
52
|
"./hex.js": "./hex.js"
|
|
51
53
|
},
|
|
52
54
|
"dependencies": {},
|