@exodus/bytes 1.0.0-rc.0 → 1.0.0-rc.10
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 +169 -2
- package/array.js +1 -1
- package/assert.js +10 -6
- package/base32.js +40 -0
- package/base58.js +212 -0
- package/base58check.js +69 -0
- package/base64.js +133 -160
- package/bech32.js +254 -0
- package/encoding-lite.js +7 -0
- package/encoding.js +12 -0
- package/fallback/_utils.js +118 -0
- package/fallback/base32.js +233 -0
- package/fallback/base64.js +192 -0
- package/fallback/encoding.js +279 -0
- package/fallback/encoding.labels.js +46 -0
- package/fallback/encoding.util.js +34 -0
- package/fallback/hex.js +127 -0
- package/fallback/latin1.js +120 -0
- package/fallback/multi-byte.encodings.cjs +1 -0
- package/fallback/multi-byte.encodings.json +545 -0
- package/fallback/multi-byte.js +448 -0
- package/fallback/multi-byte.table.js +114 -0
- package/fallback/single-byte.encodings.js +45 -0
- package/fallback/single-byte.js +83 -0
- package/fallback/utf16.js +180 -0
- package/fallback/utf8.js +245 -0
- package/hex.js +12 -71
- package/hex.node.js +28 -0
- package/multi-byte.js +13 -0
- package/multi-byte.node.js +25 -0
- package/package.json +105 -14
- package/single-byte.js +55 -0
- package/single-byte.node.js +62 -0
- package/utf16.js +73 -0
- package/utf16.node.js +79 -0
- package/utf8.js +80 -0
- package/utf8.node.js +54 -0
- package/wif.js +42 -0
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { decodeLatin1, encodeCharcodes } from './latin1.js'
|
|
2
|
+
import { isLE } from './_utils.js'
|
|
3
|
+
|
|
4
|
+
export const E_STRICT = 'Input is not well-formed utf16'
|
|
5
|
+
export const E_STRICT_UNICODE = 'Input is not well-formed Unicode'
|
|
6
|
+
|
|
7
|
+
const replacementCodepoint = 0xff_fd
|
|
8
|
+
const replacementCodepointSwapped = 0xfd_ff
|
|
9
|
+
|
|
10
|
+
const to16 = (a) => new Uint16Array(a.buffer, a.byteOffset, a.byteLength / 2) // Requires checked length and alignment!
|
|
11
|
+
|
|
12
|
+
export function to16input(u8, le) {
|
|
13
|
+
// Assume even number of bytes
|
|
14
|
+
if (le === isLE) return to16(u8.byteOffset % 2 === 0 ? u8 : Uint8Array.from(u8))
|
|
15
|
+
|
|
16
|
+
const res = new Uint8Array(u8.length)
|
|
17
|
+
|
|
18
|
+
let i = 0
|
|
19
|
+
for (const last3 = u8.length - 3; i < last3; i += 4) {
|
|
20
|
+
const x0 = u8[i]
|
|
21
|
+
const x1 = u8[i + 1]
|
|
22
|
+
const x2 = u8[i + 2]
|
|
23
|
+
const x3 = u8[i + 3]
|
|
24
|
+
res[i] = x1
|
|
25
|
+
res[i + 1] = x0
|
|
26
|
+
res[i + 2] = x3
|
|
27
|
+
res[i + 3] = x2
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
for (const last = u8.length - 1; i < last; i += 2) {
|
|
31
|
+
const x0 = u8[i]
|
|
32
|
+
const x1 = u8[i + 1]
|
|
33
|
+
res[i] = x1
|
|
34
|
+
res[i + 1] = x0
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
return to16(res)
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export const decode = (u16, loose = false, checked = false) => {
|
|
41
|
+
if (checked || isWellFormed(u16)) return decodeLatin1(u16, 0, u16.length) // it's capable of decoding Uint16Array to UTF-16 as well
|
|
42
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
43
|
+
return decodeLatin1(toWellFormed(Uint16Array.from(u16)), 0, u16.length) // cloned for replacement
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function encode(str, loose = false, checked = false, swapped = false) {
|
|
47
|
+
const arr = new Uint16Array(str.length)
|
|
48
|
+
if (checked) return swapped ? encodeCheckedSwapped(str, arr) : encodeChecked(str, arr)
|
|
49
|
+
return swapped ? encodeUncheckedSwapped(str, arr, loose) : encodeUnchecked(str, arr, loose)
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Splitting paths into small functions helps (at least on SpiderMonkey)
|
|
53
|
+
/* eslint-disable @exodus/mutable/no-param-reassign-prop-only */
|
|
54
|
+
|
|
55
|
+
const encodeChecked = (str, arr) => encodeCharcodes(str, arr) // Same as encodeLatin1, but with Uint16Array
|
|
56
|
+
|
|
57
|
+
function encodeCheckedSwapped(str, arr) {
|
|
58
|
+
// TODO: faster path for Hermes? See encodeCharcodes
|
|
59
|
+
const length = str.length
|
|
60
|
+
for (let i = 0; i < length; i++) {
|
|
61
|
+
const x = str.charCodeAt(i)
|
|
62
|
+
arr[i] = ((x & 0xff) << 8) | (x >> 8)
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return arr
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// lead: d800 - dbff, trail: dc00 - dfff
|
|
69
|
+
|
|
70
|
+
function encodeUnchecked(str, arr, loose = false) {
|
|
71
|
+
// TODO: faster path for Hermes? See encodeCharcodes
|
|
72
|
+
const length = str.length
|
|
73
|
+
for (let i = 0; i < length; i++) {
|
|
74
|
+
const code = str.charCodeAt(i)
|
|
75
|
+
arr[i] = code
|
|
76
|
+
if (code >= 0xd8_00 && code < 0xe0_00) {
|
|
77
|
+
// An unexpected trail or a lead at the very end of input
|
|
78
|
+
if (code > 0xdb_ff || i + 1 >= length) {
|
|
79
|
+
if (!loose) throw new TypeError(E_STRICT_UNICODE)
|
|
80
|
+
arr[i] = replacementCodepoint
|
|
81
|
+
} else {
|
|
82
|
+
const next = str.charCodeAt(i + 1) // Process valid pairs immediately
|
|
83
|
+
if (next < 0xdc_00 || next >= 0xe0_00) {
|
|
84
|
+
if (!loose) throw new TypeError(E_STRICT_UNICODE)
|
|
85
|
+
arr[i] = replacementCodepoint
|
|
86
|
+
} else {
|
|
87
|
+
i++ // consume next
|
|
88
|
+
arr[i] = next
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return arr
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function encodeUncheckedSwapped(str, arr, loose = false) {
|
|
98
|
+
// TODO: faster path for Hermes? See encodeCharcodes
|
|
99
|
+
const length = str.length
|
|
100
|
+
for (let i = 0; i < length; i++) {
|
|
101
|
+
const code = str.charCodeAt(i)
|
|
102
|
+
arr[i] = ((code & 0xff) << 8) | (code >> 8)
|
|
103
|
+
if (code >= 0xd8_00 && code < 0xe0_00) {
|
|
104
|
+
// An unexpected trail or a lead at the very end of input
|
|
105
|
+
if (code > 0xdb_ff || i + 1 >= length) {
|
|
106
|
+
if (!loose) throw new TypeError(E_STRICT_UNICODE)
|
|
107
|
+
arr[i] = replacementCodepointSwapped
|
|
108
|
+
} else {
|
|
109
|
+
const next = str.charCodeAt(i + 1) // Process valid pairs immediately
|
|
110
|
+
if (next < 0xdc_00 || next >= 0xe0_00) {
|
|
111
|
+
if (!loose) throw new TypeError(E_STRICT_UNICODE)
|
|
112
|
+
arr[i] = replacementCodepointSwapped
|
|
113
|
+
} else {
|
|
114
|
+
i++ // consume next
|
|
115
|
+
arr[i] = ((next & 0xff) << 8) | (next >> 8)
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return arr
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export function toWellFormed(u16) {
|
|
125
|
+
const length = u16.length
|
|
126
|
+
for (let i = 0; i < length; i++) {
|
|
127
|
+
const code = u16[i]
|
|
128
|
+
if (code >= 0xd8_00 && code < 0xe0_00) {
|
|
129
|
+
// An unexpected trail or a lead at the very end of input
|
|
130
|
+
if (code > 0xdb_ff || i + 1 >= length) {
|
|
131
|
+
u16[i] = replacementCodepoint
|
|
132
|
+
} else {
|
|
133
|
+
const next = u16[i + 1] // Process valid pairs immediately
|
|
134
|
+
if (next < 0xdc_00 || next >= 0xe0_00) {
|
|
135
|
+
u16[i] = replacementCodepoint
|
|
136
|
+
} else {
|
|
137
|
+
i++ // consume next
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
return u16
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function isWellFormed(u16) {
|
|
147
|
+
const length = u16.length
|
|
148
|
+
let i = 0
|
|
149
|
+
|
|
150
|
+
// Speedup with u32, by skipping to the first surrogate
|
|
151
|
+
// Only implemented for aligned input for now, but almost all input is aligned (pooled Buffer or 0 offset)
|
|
152
|
+
if (length > 32 && u16.byteOffset % 4 === 0) {
|
|
153
|
+
const u32length = (u16.byteLength / 4) | 0
|
|
154
|
+
const u32 = new Uint32Array(u16.buffer, u16.byteOffset, u32length)
|
|
155
|
+
for (const last3 = u32length - 3; ; i += 4) {
|
|
156
|
+
if (i >= last3) break // loop is fast enough for moving this here to be _very_ useful, likely due to array access checks
|
|
157
|
+
const a = u32[i]
|
|
158
|
+
const b = u32[i + 1]
|
|
159
|
+
const c = u32[i + 2]
|
|
160
|
+
const d = u32[i + 3]
|
|
161
|
+
if (a & 0x80_00_80_00 || b & 0x80_00_80_00 || c & 0x80_00_80_00 || d & 0x80_00_80_00) break
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (; i < u32length; i++) if (u32[i] & 0x80_00_80_00) break
|
|
165
|
+
i *= 2
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (; i < length; i++) {
|
|
169
|
+
const code = u16[i]
|
|
170
|
+
if (code >= 0xd8_00 && code < 0xe0_00) {
|
|
171
|
+
// An unexpected trail or a lead at the very end of input
|
|
172
|
+
if (code > 0xdb_ff || i + 1 >= length) return false
|
|
173
|
+
i++ // consume next
|
|
174
|
+
const next = u16[i] // Process valid pairs immediately
|
|
175
|
+
if (next < 0xdc_00 || next >= 0xe0_00) return false
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return true
|
|
180
|
+
}
|
package/fallback/utf8.js
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
export const E_STRICT = 'Input is not well-formed utf8'
|
|
2
|
+
export const E_STRICT_UNICODE = 'Input is not well-formed Unicode'
|
|
3
|
+
|
|
4
|
+
const replacementPoint = 0xff_fd
|
|
5
|
+
|
|
6
|
+
// https://encoding.spec.whatwg.org/#utf-8-decoder
|
|
7
|
+
// We are most likely in loose mode, for non-loose escape & decodeURIComponent solved everything
|
|
8
|
+
export function decode(arr, loose, start = 0) {
|
|
9
|
+
start |= 0
|
|
10
|
+
const end = arr.length
|
|
11
|
+
let out = ''
|
|
12
|
+
const chunkSize = 0x2_00 // far below MAX_ARGUMENTS_LENGTH in npmjs.com/buffer, we use smaller chunks
|
|
13
|
+
const tmpSize = Math.min(end - start, chunkSize + 1) // need 1 extra slot for last codepoint, which can be 2 charcodes
|
|
14
|
+
const tmp = new Array(tmpSize).fill(0)
|
|
15
|
+
let ti = 0
|
|
16
|
+
|
|
17
|
+
for (let i = start; i < end; i++) {
|
|
18
|
+
if (ti >= chunkSize) {
|
|
19
|
+
tmp.length = ti // can be larger by 1 if last codepoint is two charcodes
|
|
20
|
+
out += String.fromCharCode.apply(String, tmp)
|
|
21
|
+
if (tmp.length <= chunkSize) tmp.push(0) // restore 1 extra slot for last codepoint
|
|
22
|
+
ti = 0
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const byte = arr[i]
|
|
26
|
+
if (byte < 0x80) {
|
|
27
|
+
tmp[ti++] = byte
|
|
28
|
+
// ascii fast path is in ../utf8.js, this is called only on non-ascii input
|
|
29
|
+
// so we don't unroll this anymore
|
|
30
|
+
} else if (byte < 0xc2) {
|
|
31
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
32
|
+
tmp[ti++] = replacementPoint
|
|
33
|
+
} else if (byte < 0xe0) {
|
|
34
|
+
// need 1 more
|
|
35
|
+
if (i + 1 >= end) {
|
|
36
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
37
|
+
tmp[ti++] = replacementPoint
|
|
38
|
+
break
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const byte1 = arr[i + 1]
|
|
42
|
+
if (byte1 < 0x80 || byte1 > 0xbf) {
|
|
43
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
44
|
+
tmp[ti++] = replacementPoint
|
|
45
|
+
continue
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
i++
|
|
49
|
+
tmp[ti++] = ((byte & 0x1f) << 6) | (byte1 & 0x3f)
|
|
50
|
+
} else if (byte < 0xf0) {
|
|
51
|
+
// need 2 more
|
|
52
|
+
if (i + 1 >= end) {
|
|
53
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
54
|
+
tmp[ti++] = replacementPoint
|
|
55
|
+
break
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const lower = byte === 0xe0 ? 0xa0 : 0x80
|
|
59
|
+
const upper = byte === 0xed ? 0x9f : 0xbf
|
|
60
|
+
const byte1 = arr[i + 1]
|
|
61
|
+
if (byte1 < lower || byte1 > upper) {
|
|
62
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
63
|
+
tmp[ti++] = replacementPoint
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
i++
|
|
68
|
+
if (i + 1 >= end) {
|
|
69
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
70
|
+
tmp[ti++] = replacementPoint
|
|
71
|
+
break
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const byte2 = arr[i + 1]
|
|
75
|
+
if (byte2 < 0x80 || byte2 > 0xbf) {
|
|
76
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
77
|
+
tmp[ti++] = replacementPoint
|
|
78
|
+
continue
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
i++
|
|
82
|
+
tmp[ti++] = ((byte & 0xf) << 12) | ((byte1 & 0x3f) << 6) | (byte2 & 0x3f)
|
|
83
|
+
} else if (byte <= 0xf4) {
|
|
84
|
+
// need 3 more
|
|
85
|
+
if (i + 1 >= end) {
|
|
86
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
87
|
+
tmp[ti++] = replacementPoint
|
|
88
|
+
break
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const lower = byte === 0xf0 ? 0x90 : 0x80
|
|
92
|
+
const upper = byte === 0xf4 ? 0x8f : 0xbf
|
|
93
|
+
const byte1 = arr[i + 1]
|
|
94
|
+
if (byte1 < lower || byte1 > upper) {
|
|
95
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
96
|
+
tmp[ti++] = replacementPoint
|
|
97
|
+
continue
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
i++
|
|
101
|
+
if (i + 1 >= end) {
|
|
102
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
103
|
+
tmp[ti++] = replacementPoint
|
|
104
|
+
break
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const byte2 = arr[i + 1]
|
|
108
|
+
if (byte2 < 0x80 || byte2 > 0xbf) {
|
|
109
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
110
|
+
tmp[ti++] = replacementPoint
|
|
111
|
+
continue
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
i++
|
|
115
|
+
if (i + 1 >= end) {
|
|
116
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
117
|
+
tmp[ti++] = replacementPoint
|
|
118
|
+
break
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const byte3 = arr[i + 1]
|
|
122
|
+
if (byte3 < 0x80 || byte3 > 0xbf) {
|
|
123
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
124
|
+
tmp[ti++] = replacementPoint
|
|
125
|
+
continue
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
i++
|
|
129
|
+
const codePoint =
|
|
130
|
+
((byte & 0xf) << 18) | ((byte1 & 0x3f) << 12) | ((byte2 & 0x3f) << 6) | (byte3 & 0x3f)
|
|
131
|
+
if (codePoint > 0xff_ff) {
|
|
132
|
+
// split into char codes as String.fromCharCode is faster than String.fromCodePoint
|
|
133
|
+
const u = codePoint - 0x1_00_00
|
|
134
|
+
tmp[ti++] = 0xd8_00 + ((u >> 10) & 0x3_ff)
|
|
135
|
+
tmp[ti++] = 0xdc_00 + (u & 0x3_ff)
|
|
136
|
+
} else {
|
|
137
|
+
tmp[ti++] = codePoint
|
|
138
|
+
}
|
|
139
|
+
// eslint-disable-next-line sonarjs/no-duplicated-branches
|
|
140
|
+
} else {
|
|
141
|
+
if (!loose) throw new TypeError(E_STRICT)
|
|
142
|
+
tmp[ti++] = replacementPoint
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (ti === 0) return out
|
|
147
|
+
tmp.length = ti
|
|
148
|
+
return out + String.fromCharCode.apply(String, tmp)
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function encode(string, loose) {
|
|
152
|
+
const length = string.length
|
|
153
|
+
let small = true
|
|
154
|
+
let bytes = new Uint8Array(length) // assume ascii
|
|
155
|
+
let p = 0
|
|
156
|
+
|
|
157
|
+
for (let i = 0; i < length; i++) {
|
|
158
|
+
let code = string.charCodeAt(i)
|
|
159
|
+
if (code < 0x80) {
|
|
160
|
+
bytes[p++] = code
|
|
161
|
+
// Unroll the loop a bit for faster ops
|
|
162
|
+
while (true) {
|
|
163
|
+
i++
|
|
164
|
+
if (i >= length) break
|
|
165
|
+
code = string.charCodeAt(i)
|
|
166
|
+
if (code >= 0x80) break
|
|
167
|
+
bytes[p++] = code
|
|
168
|
+
i++
|
|
169
|
+
if (i >= length) break
|
|
170
|
+
code = string.charCodeAt(i)
|
|
171
|
+
if (code >= 0x80) break
|
|
172
|
+
bytes[p++] = code
|
|
173
|
+
i++
|
|
174
|
+
if (i >= length) break
|
|
175
|
+
code = string.charCodeAt(i)
|
|
176
|
+
if (code >= 0x80) break
|
|
177
|
+
bytes[p++] = code
|
|
178
|
+
i++
|
|
179
|
+
if (i >= length) break
|
|
180
|
+
code = string.charCodeAt(i)
|
|
181
|
+
if (code >= 0x80) break
|
|
182
|
+
bytes[p++] = code
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (i >= length) break
|
|
186
|
+
// now, code is present and >= 0x80
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (small) {
|
|
190
|
+
// TODO: use resizable array buffers? will have to return a non-resizeable one
|
|
191
|
+
if (p !== i) throw new Error('Unreachable') // Here, p === i (only when small is still true)
|
|
192
|
+
const bytesNew = new Uint8Array(p + (length - i) * 3) // maximium can be 3x of the string length in charcodes
|
|
193
|
+
bytesNew.set(bytes)
|
|
194
|
+
bytes = bytesNew
|
|
195
|
+
small = false
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// surrogate, charcodes = [d800 + a & 3ff, dc00 + b & 3ff]; codePoint = 0x1_00_00 | (a << 10) | b
|
|
199
|
+
// lead: d800 - dbff
|
|
200
|
+
// trail: dc00 - dfff
|
|
201
|
+
if (code >= 0xd8_00 && code < 0xe0_00) {
|
|
202
|
+
// Can't be a valid trail as we already processed that below
|
|
203
|
+
|
|
204
|
+
if (code > 0xdb_ff || i + 1 >= length) {
|
|
205
|
+
// An unexpected trail or a lead at the very end of input
|
|
206
|
+
if (!loose) throw new TypeError(E_STRICT_UNICODE)
|
|
207
|
+
bytes[p++] = 0xef
|
|
208
|
+
bytes[p++] = 0xbf
|
|
209
|
+
bytes[p++] = 0xbd
|
|
210
|
+
continue
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const next = string.charCodeAt(i + 1) // Process valid pairs immediately
|
|
214
|
+
if (next >= 0xdc_00 && next < 0xe0_00) {
|
|
215
|
+
// here, codePoint is always between 0x1_00_00 and 0x11_00_00, we encode as 4 bytes
|
|
216
|
+
const codePoint = (((code - 0xd8_00) << 10) | (next - 0xdc_00)) + 0x1_00_00
|
|
217
|
+
bytes[p++] = (codePoint >> 18) | 0xf0
|
|
218
|
+
bytes[p++] = ((codePoint >> 12) & 0x3f) | 0x80
|
|
219
|
+
bytes[p++] = ((codePoint >> 6) & 0x3f) | 0x80
|
|
220
|
+
bytes[p++] = (codePoint & 0x3f) | 0x80
|
|
221
|
+
i++ // consume next
|
|
222
|
+
} else {
|
|
223
|
+
// Next is not a trail, leave next unconsumed but process unmatched lead error
|
|
224
|
+
if (!loose) throw new TypeError(E_STRICT_UNICODE)
|
|
225
|
+
bytes[p++] = 0xef
|
|
226
|
+
bytes[p++] = 0xbf
|
|
227
|
+
bytes[p++] = 0xbd
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
continue
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// We are left with a non-pair char code above ascii, it gets encoded to 2 or 3 bytes
|
|
234
|
+
if (code < 0x8_00) {
|
|
235
|
+
bytes[p++] = (code >> 6) | 0xc0
|
|
236
|
+
bytes[p++] = (code & 0x3f) | 0x80
|
|
237
|
+
} else {
|
|
238
|
+
bytes[p++] = (code >> 12) | 0xe0
|
|
239
|
+
bytes[p++] = ((code >> 6) & 0x3f) | 0x80
|
|
240
|
+
bytes[p++] = (code & 0x3f) | 0x80
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
return bytes.length === p ? bytes : bytes.slice(0, p)
|
|
245
|
+
}
|
package/hex.js
CHANGED
|
@@ -1,78 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { assertUint8 } from './assert.js'
|
|
2
|
+
import { typedView } from './array.js'
|
|
3
|
+
import { skipWeb } from './fallback/_utils.js'
|
|
4
|
+
import * as js from './fallback/hex.js'
|
|
3
5
|
|
|
4
|
-
const { Buffer } = globalThis // Buffer is optional, only used when native
|
|
5
|
-
const haveNativeBuffer = Buffer && !Buffer.TYPED_ARRAY_SUPPORT
|
|
6
6
|
const { toHex: webHex } = Uint8Array.prototype // Modern engines have this
|
|
7
7
|
|
|
8
|
-
let hexArray
|
|
9
|
-
let dehexArray
|
|
10
|
-
|
|
11
8
|
export function toHex(arr) {
|
|
12
|
-
|
|
13
|
-
if (
|
|
14
|
-
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')
|
|
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 > 3000) {
|
|
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
|
|
9
|
+
assertUint8(arr)
|
|
10
|
+
if (arr.length === 0) return ''
|
|
11
|
+
if (!skipWeb && webHex && arr.toHex === webHex) return arr.toHex()
|
|
12
|
+
return js.toHex(arr)
|
|
43
13
|
}
|
|
44
14
|
|
|
45
15
|
// Unlike Buffer.from(), throws on invalid input
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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 }
|
|
16
|
+
export const fromHex =
|
|
17
|
+
!skipWeb && Uint8Array.fromHex
|
|
18
|
+
? (str, format = 'uint8') => typedView(Uint8Array.fromHex(str), format)
|
|
19
|
+
: (str, format = 'uint8') => typedView(js.fromHex(str), format)
|
package/hex.node.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
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
|
+
const denoBug = Buffer.from('ag', 'hex').length > 0
|
|
9
|
+
|
|
10
|
+
export function toHex(arr) {
|
|
11
|
+
assertUint8(arr)
|
|
12
|
+
if (arr.length === 0) return ''
|
|
13
|
+
if (webHex && arr.toHex === webHex) return arr.toHex()
|
|
14
|
+
if (arr.constructor === Buffer && Buffer.isBuffer(arr)) return arr.hexSlice(0, arr.byteLength)
|
|
15
|
+
return Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength).hexSlice(0, arr.byteLength)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Unlike Buffer.from(), throws on invalid input
|
|
19
|
+
export const fromHex = Uint8Array.fromHex
|
|
20
|
+
? (str, format = 'uint8') => typedView(Uint8Array.fromHex(str), format)
|
|
21
|
+
: (str, format = 'uint8') => {
|
|
22
|
+
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
23
|
+
if (str.length % 2 !== 0) throw new SyntaxError(E_HEX)
|
|
24
|
+
if (denoBug && /[^\dA-Fa-f]/.test(str)) throw new SyntaxError(E_HEX)
|
|
25
|
+
const buf = Buffer.from(str, 'hex') // will stop on first non-hex character, so we can just validate length
|
|
26
|
+
if (buf.length * 2 !== str.length) throw new SyntaxError(E_HEX)
|
|
27
|
+
return typedView(buf, format)
|
|
28
|
+
}
|
package/multi-byte.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { assertUint8 } from './assert.js'
|
|
2
|
+
import { multibyteDecoder } from './fallback/multi-byte.js'
|
|
3
|
+
|
|
4
|
+
export function createMultibyteDecoder(encoding, loose = false) {
|
|
5
|
+
const jsDecoder = multibyteDecoder(encoding, loose) // asserts
|
|
6
|
+
let streaming = false
|
|
7
|
+
return (arr, stream = false) => {
|
|
8
|
+
assertUint8(arr)
|
|
9
|
+
if (!streaming && arr.byteLength === 0) return ''
|
|
10
|
+
streaming = stream
|
|
11
|
+
return jsDecoder(arr, stream)
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { assertUint8 } from './assert.js'
|
|
2
|
+
import { isDeno } from './fallback/_utils.js'
|
|
3
|
+
import { isAsciiSuperset, multibyteDecoder } from './fallback/multi-byte.js'
|
|
4
|
+
import { isAscii } from 'node:buffer'
|
|
5
|
+
|
|
6
|
+
const toBuf = (x) => Buffer.from(x.buffer, x.byteOffset, x.byteLength)
|
|
7
|
+
|
|
8
|
+
export function createMultibyteDecoder(encoding, loose = false) {
|
|
9
|
+
const jsDecoder = multibyteDecoder(encoding, loose) // asserts
|
|
10
|
+
let streaming = false
|
|
11
|
+
const asciiSuperset = isAsciiSuperset(encoding)
|
|
12
|
+
return (arr, stream = false) => {
|
|
13
|
+
assertUint8(arr)
|
|
14
|
+
if (!streaming) {
|
|
15
|
+
if (arr.byteLength === 0) return ''
|
|
16
|
+
if (asciiSuperset && isAscii(arr)) {
|
|
17
|
+
if (isDeno) return toBuf(arr).toString()
|
|
18
|
+
return toBuf(arr).latin1Slice(0, arr.byteLength) // .latin1Slice is faster than .asciiSlice
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
streaming = stream
|
|
23
|
+
return jsDecoder(arr, stream)
|
|
24
|
+
}
|
|
25
|
+
}
|