@exodus/bytes 1.0.0-rc.8 → 1.0.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/LICENSE +1 -1
- package/README.md +286 -4
- package/array.d.ts +24 -0
- package/base58.js +16 -8
- package/base64.d.ts +76 -0
- package/bigint.js +14 -0
- package/encoding-lite.js +7 -0
- package/encoding.js +12 -0
- package/fallback/_utils.js +100 -10
- package/fallback/encoding.js +290 -0
- package/fallback/encoding.labels.js +46 -0
- package/fallback/encoding.util.js +34 -0
- package/fallback/hex.js +2 -70
- package/fallback/latin1.js +2 -1
- 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 +61 -0
- package/fallback/single-byte.js +86 -0
- package/fallback/utf16.js +180 -0
- package/hex.d.ts +22 -0
- package/hex.node.js +2 -0
- package/multi-byte.js +13 -0
- package/multi-byte.node.js +25 -0
- package/package.json +62 -13
- package/single-byte.js +54 -0
- package/single-byte.node.js +62 -0
- package/utf16.js +73 -0
- package/utf16.node.js +79 -0
- package/utf8.d.ts +42 -0
- package/utf8.js +7 -9
- package/utf8.node.js +8 -5
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { asciiPrefix, decodeLatin1 } from './latin1.js'
|
|
2
|
+
import encodings from './single-byte.encodings.js'
|
|
3
|
+
import { decode2string } from './_utils.js'
|
|
4
|
+
|
|
5
|
+
export const E_STRICT = 'Input is not well-formed for this encoding'
|
|
6
|
+
const xUserDefined = 'x-user-defined'
|
|
7
|
+
const iso8i = 'iso-8859-8-i'
|
|
8
|
+
|
|
9
|
+
export const assertEncoding = (encoding) => {
|
|
10
|
+
if (Object.hasOwn(encodings, encoding) || encoding === xUserDefined || encoding === iso8i) return
|
|
11
|
+
throw new RangeError('Unsupported encoding')
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const r = 0xff_fd
|
|
15
|
+
|
|
16
|
+
function getEncoding(encoding) {
|
|
17
|
+
assertEncoding(encoding)
|
|
18
|
+
if (encoding === xUserDefined) return Array.from({ length: 128 }, (_, i) => 0xf7_80 + i)
|
|
19
|
+
if (encoding === iso8i) encoding = 'iso-8859-8'
|
|
20
|
+
let prev = 127
|
|
21
|
+
return encodings[encoding].map((x) => (x === r ? x : (prev += x))) // eslint-disable-line no-return-assign
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const mappers = new Map()
|
|
25
|
+
const decoders = new Map()
|
|
26
|
+
|
|
27
|
+
// Used only on Node.js, no reason to optimize for anything else
|
|
28
|
+
// E.g. avoiding .from and filling zero-initialized arr manually is faster on Hermes, but we avoid this codepath on Hermes completely
|
|
29
|
+
export function encodingMapper(encoding) {
|
|
30
|
+
const cached = mappers.get(encoding)
|
|
31
|
+
if (cached) return cached
|
|
32
|
+
|
|
33
|
+
const codes = getEncoding(encoding)
|
|
34
|
+
const incomplete = codes.includes(0xff_fd)
|
|
35
|
+
let map
|
|
36
|
+
const mapper = (arr, start = 0) => {
|
|
37
|
+
if (!map) {
|
|
38
|
+
map = new Uint16Array(256).map((_, i) => i) // Unicode subset
|
|
39
|
+
map.set(Uint16Array.from(codes), 128)
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const o = Uint16Array.from(start === 0 ? arr : arr.subarray(start)) // copy to modify in-place, also those are 16-bit now
|
|
43
|
+
let i = 0
|
|
44
|
+
for (const end7 = o.length - 7; i < end7; i += 8) {
|
|
45
|
+
o[i] = map[o[i]]
|
|
46
|
+
o[i + 1] = map[o[i + 1]]
|
|
47
|
+
o[i + 2] = map[o[i + 2]]
|
|
48
|
+
o[i + 3] = map[o[i + 3]]
|
|
49
|
+
o[i + 4] = map[o[i + 4]]
|
|
50
|
+
o[i + 5] = map[o[i + 5]]
|
|
51
|
+
o[i + 6] = map[o[i + 6]]
|
|
52
|
+
o[i + 7] = map[o[i + 7]]
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
for (const end = o.length; i < end; i++) o[i] = map[o[i]]
|
|
56
|
+
return o
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
mappers.set(encoding, { mapper, incomplete })
|
|
60
|
+
return { mapper, incomplete }
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function encodingDecoder(encoding) {
|
|
64
|
+
const cached = decoders.get(encoding)
|
|
65
|
+
if (cached) return cached
|
|
66
|
+
|
|
67
|
+
let strings
|
|
68
|
+
const codes = getEncoding(encoding)
|
|
69
|
+
const incomplete = codes.includes(0xff_fd)
|
|
70
|
+
const decoder = (arr, loose = false) => {
|
|
71
|
+
if (!strings) {
|
|
72
|
+
const allCodes = Array.from({ length: 128 }, (_, i) => i).concat(codes)
|
|
73
|
+
while (allCodes.length < 256) allCodes.push(allCodes.length)
|
|
74
|
+
strings = allCodes.map((c) => String.fromCharCode(c))
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const prefix = decodeLatin1(arr, 0, asciiPrefix(arr))
|
|
78
|
+
if (prefix.length === arr.length) return prefix
|
|
79
|
+
const suffix = decode2string(arr, prefix.length, arr.length, strings)
|
|
80
|
+
if (!loose && incomplete && suffix.includes('\uFFFD')) throw new TypeError(E_STRICT)
|
|
81
|
+
return prefix + suffix
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
decoders.set(encoding, decoder)
|
|
85
|
+
return decoder
|
|
86
|
+
}
|
|
@@ -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/hex.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/// <reference types="node" />
|
|
2
|
+
|
|
3
|
+
import type { OutputFormat, Uint8ArrayBuffer } from './array.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Encodes a Uint8Array to a lowercase hex string
|
|
7
|
+
* @param arr - The input bytes
|
|
8
|
+
* @returns The hex encoded string
|
|
9
|
+
*/
|
|
10
|
+
export function toHex(arr: Uint8ArrayBuffer): string;
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Decodes a hex string to bytes
|
|
14
|
+
* Unlike Buffer.from(), throws on invalid input
|
|
15
|
+
* @param str - The hex encoded string (case-insensitive)
|
|
16
|
+
* @param format - Output format (default: 'uint8')
|
|
17
|
+
* @returns The decoded bytes
|
|
18
|
+
*/
|
|
19
|
+
export function fromHex(str: string, format?: 'uint8'): Uint8ArrayBuffer;
|
|
20
|
+
export function fromHex(str: string, format: 'buffer'): Buffer;
|
|
21
|
+
export function fromHex(str: string, format?: OutputFormat): Uint8ArrayBuffer | Buffer;
|
|
22
|
+
|
package/hex.node.js
CHANGED
|
@@ -5,6 +5,7 @@ import { E_HEX } from './fallback/hex.js'
|
|
|
5
5
|
if (Buffer.TYPED_ARRAY_SUPPORT) throw new Error('Unexpected Buffer polyfill')
|
|
6
6
|
|
|
7
7
|
const { toHex: webHex } = Uint8Array.prototype // Modern engines have this
|
|
8
|
+
const denoBug = Buffer.from('ag', 'hex').length > 0
|
|
8
9
|
|
|
9
10
|
export function toHex(arr) {
|
|
10
11
|
assertUint8(arr)
|
|
@@ -20,6 +21,7 @@ export const fromHex = Uint8Array.fromHex
|
|
|
20
21
|
: (str, format = 'uint8') => {
|
|
21
22
|
if (typeof str !== 'string') throw new TypeError('Input is not a string')
|
|
22
23
|
if (str.length % 2 !== 0) throw new SyntaxError(E_HEX)
|
|
24
|
+
if (denoBug && /[^\dA-Fa-f]/.test(str)) throw new SyntaxError(E_HEX)
|
|
23
25
|
const buf = Buffer.from(str, 'hex') // will stop on first non-hex character, so we can just validate length
|
|
24
26
|
if (buf.length * 2 !== str.length) throw new SyntaxError(E_HEX)
|
|
25
27
|
return typedView(buf, format)
|
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
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exodus/bytes",
|
|
3
|
-
"version": "1.0.0
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "Various operations on Uint8Array data",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"lint": "eslint .",
|
|
@@ -10,6 +10,10 @@
|
|
|
10
10
|
"test:spidermonkey": "exodus-test --engine=spidermonkey:bundle",
|
|
11
11
|
"test:hermes": "exodus-test --engine=hermes:bundle",
|
|
12
12
|
"test:quickjs": "exodus-test --engine=quickjs:bundle",
|
|
13
|
+
"test:xs": "exodus-test --engine=xs:bundle",
|
|
14
|
+
"test:engine262": "exodus-test --engine=engine262:bundle",
|
|
15
|
+
"test:deno": "exodus-test --engine=deno:pure",
|
|
16
|
+
"test:bun": "exodus-test --engine=bun:pure",
|
|
13
17
|
"test:electron:bundle": "exodus-test --engine=electron:bundle",
|
|
14
18
|
"test:electron:as-node": "exodus-test --engine=electron-as-node:test",
|
|
15
19
|
"test:chrome:puppeteer": "exodus-test --engine=chrome:puppeteer",
|
|
@@ -17,6 +21,7 @@
|
|
|
17
21
|
"test:webkit:playwright": "exodus-test --engine=webkit:playwright",
|
|
18
22
|
"test:firefox:puppeteer": "exodus-test --engine=firefox:puppeteer",
|
|
19
23
|
"test:firefox:playwright": "exodus-test --engine=firefox:playwright",
|
|
24
|
+
"test:servo:bundle": "exodus-test --engine=servo:bundle",
|
|
20
25
|
"test": "exodus-test",
|
|
21
26
|
"jsvu": "jsvu",
|
|
22
27
|
"playwright": "exodus-test --playwright",
|
|
@@ -25,54 +30,96 @@
|
|
|
25
30
|
},
|
|
26
31
|
"repository": {
|
|
27
32
|
"type": "git",
|
|
28
|
-
"url": "git+https://github.com/
|
|
33
|
+
"url": "git+https://github.com/ExodusOSS/bytes.git"
|
|
29
34
|
},
|
|
30
35
|
"author": "Exodus Movement, Inc.",
|
|
31
36
|
"license": "MIT",
|
|
32
37
|
"bugs": {
|
|
33
|
-
"url": "https://github.com/
|
|
38
|
+
"url": "https://github.com/ExodusOSS/bytes/issues"
|
|
34
39
|
},
|
|
35
|
-
"homepage": "https://github.com/
|
|
40
|
+
"homepage": "https://github.com/ExodusOSS/bytes#readme",
|
|
36
41
|
"engines": {
|
|
37
|
-
"node": "^20.19.0 ||
|
|
42
|
+
"node": "^20.19.0 || ^22.12.0 || >=24.0.0"
|
|
38
43
|
},
|
|
39
44
|
"type": "module",
|
|
40
45
|
"files": [
|
|
41
46
|
"/fallback/_utils.js",
|
|
42
|
-
"/fallback/latin1.js",
|
|
43
47
|
"/fallback/base32.js",
|
|
44
48
|
"/fallback/base64.js",
|
|
49
|
+
"/fallback/encoding.js",
|
|
50
|
+
"/fallback/encoding.labels.js",
|
|
51
|
+
"/fallback/encoding.util.js",
|
|
45
52
|
"/fallback/hex.js",
|
|
53
|
+
"/fallback/latin1.js",
|
|
54
|
+
"/fallback/multi-byte.encodings.cjs",
|
|
55
|
+
"/fallback/multi-byte.encodings.json",
|
|
56
|
+
"/fallback/multi-byte.js",
|
|
57
|
+
"/fallback/multi-byte.table.js",
|
|
58
|
+
"/fallback/single-byte.encodings.js",
|
|
59
|
+
"/fallback/single-byte.js",
|
|
60
|
+
"/fallback/utf16.js",
|
|
46
61
|
"/fallback/utf8.js",
|
|
47
62
|
"/array.js",
|
|
63
|
+
"/array.d.ts",
|
|
48
64
|
"/assert.js",
|
|
49
65
|
"/base32.js",
|
|
50
66
|
"/base58.js",
|
|
51
67
|
"/base58check.js",
|
|
52
68
|
"/base64.js",
|
|
69
|
+
"/base64.d.ts",
|
|
53
70
|
"/bech32.js",
|
|
54
|
-
"/
|
|
71
|
+
"/bigint.js",
|
|
72
|
+
"/encoding.js",
|
|
73
|
+
"/encoding-lite.js",
|
|
55
74
|
"/hex.js",
|
|
56
|
-
"/
|
|
75
|
+
"/hex.d.ts",
|
|
76
|
+
"/hex.node.js",
|
|
77
|
+
"/multi-byte.js",
|
|
78
|
+
"/multi-byte.node.js",
|
|
79
|
+
"/single-byte.js",
|
|
80
|
+
"/single-byte.node.js",
|
|
81
|
+
"/utf16.js",
|
|
82
|
+
"/utf16.node.js",
|
|
57
83
|
"/utf8.js",
|
|
84
|
+
"/utf8.d.ts",
|
|
85
|
+
"/utf8.node.js",
|
|
58
86
|
"/wif.js"
|
|
59
87
|
],
|
|
60
88
|
"exports": {
|
|
61
|
-
"./array.js":
|
|
89
|
+
"./array.js": {
|
|
90
|
+
"types": "./array.d.ts",
|
|
91
|
+
"default": "./array.js"
|
|
92
|
+
},
|
|
62
93
|
"./base32.js": "./base32.js",
|
|
63
94
|
"./base58.js": "./base58.js",
|
|
64
95
|
"./base58check.js": "./base58check.js",
|
|
65
|
-
"./base64.js":
|
|
96
|
+
"./base64.js": {
|
|
97
|
+
"types": "./base64.d.ts",
|
|
98
|
+
"default": "./base64.js"
|
|
99
|
+
},
|
|
66
100
|
"./bech32.js": "./bech32.js",
|
|
101
|
+
"./bigint.js": "./bigint.js",
|
|
67
102
|
"./hex.js": {
|
|
103
|
+
"types": "./hex.d.ts",
|
|
68
104
|
"node": "./hex.node.js",
|
|
69
105
|
"default": "./hex.js"
|
|
70
106
|
},
|
|
107
|
+
"./multi-byte.js": {
|
|
108
|
+
"node": "./multi-byte.node.js",
|
|
109
|
+
"default": "./multi-byte.js"
|
|
110
|
+
},
|
|
111
|
+
"./single-byte.js": {
|
|
112
|
+
"node": "./single-byte.node.js",
|
|
113
|
+
"default": "./single-byte.js"
|
|
114
|
+
},
|
|
115
|
+
"./encoding.js": "./encoding.js",
|
|
116
|
+
"./encoding-lite.js": "./encoding-lite.js",
|
|
71
117
|
"./utf16.js": {
|
|
72
118
|
"node": "./utf16.node.js",
|
|
73
119
|
"default": "./utf16.js"
|
|
74
120
|
},
|
|
75
121
|
"./utf8.js": {
|
|
122
|
+
"types": "./utf8.d.ts",
|
|
76
123
|
"node": "./utf8.node.js",
|
|
77
124
|
"default": "./utf8.js"
|
|
78
125
|
},
|
|
@@ -91,14 +138,15 @@
|
|
|
91
138
|
"@exodus/crypto": "^1.0.0-rc.30",
|
|
92
139
|
"@exodus/eslint-config": "^5.24.0",
|
|
93
140
|
"@exodus/prettier": "^1.0.0",
|
|
94
|
-
"@exodus/test": "^1.0.0-rc.
|
|
141
|
+
"@exodus/test": "^1.0.0-rc.109",
|
|
95
142
|
"@noble/hashes": "^2.0.1",
|
|
96
143
|
"@scure/base": "^1.2.6",
|
|
97
144
|
"@stablelib/base64": "^2.0.1",
|
|
98
145
|
"@stablelib/hex": "^2.0.1",
|
|
99
|
-
"@types/node": "^22.
|
|
146
|
+
"@types/node": "^22.12.0",
|
|
100
147
|
"base-x": "^5.0.1",
|
|
101
148
|
"base32.js": "^0.1.0",
|
|
149
|
+
"base58-js": "^3.0.3",
|
|
102
150
|
"base64-js": "^1.5.1",
|
|
103
151
|
"bech32": "^2.0.0",
|
|
104
152
|
"bs58": "^6.0.0",
|
|
@@ -114,11 +162,12 @@
|
|
|
114
162
|
"hextreme": "^1.0.7",
|
|
115
163
|
"hi-base32": "^0.5.1",
|
|
116
164
|
"iconv-lite": "^0.7.0",
|
|
117
|
-
"jsvu": "^3.0.
|
|
165
|
+
"jsvu": "^3.0.3",
|
|
118
166
|
"text-encoding": "^0.7.0",
|
|
119
167
|
"typescript": "^5.9.3",
|
|
120
168
|
"uint8array-tools": "^0.0.9",
|
|
121
169
|
"utf8": "^3.0.0",
|
|
170
|
+
"whatwg-encoding": "^3.1.1",
|
|
122
171
|
"wif": "^5.0.0"
|
|
123
172
|
},
|
|
124
173
|
"prettier": "@exodus/prettier",
|
package/single-byte.js
ADDED
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { assertUint8 } from './assert.js'
|
|
2
|
+
import { canDecoders } from './fallback/_utils.js'
|
|
3
|
+
import { assertEncoding, encodingDecoder } from './fallback/single-byte.js'
|
|
4
|
+
|
|
5
|
+
const { TextDecoder } = globalThis
|
|
6
|
+
|
|
7
|
+
let windows1252works
|
|
8
|
+
|
|
9
|
+
function shouldUseNative(enc) {
|
|
10
|
+
// https://issues.chromium.org/issues/468458388
|
|
11
|
+
// Also might be incorrectly imlemented on platforms as Latin1 (e.g. in Node.js) or regress
|
|
12
|
+
// This is the most significant single-byte encoding, 'ascii' and 'latin1' alias to this
|
|
13
|
+
// Even after Chrome bug is fixed, this should serve as a quick correctness check that it's actually windows-1252
|
|
14
|
+
if (enc === 'windows-1252') {
|
|
15
|
+
if (windows1252works === undefined) {
|
|
16
|
+
windows1252works = false
|
|
17
|
+
try {
|
|
18
|
+
const u = new Uint8Array(9) // using 9 bytes is significant to catch the bug
|
|
19
|
+
u[8] = 128
|
|
20
|
+
windows1252works = new TextDecoder(enc).decode(u).codePointAt(8) === 0x20_ac
|
|
21
|
+
} catch {}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return windows1252works
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// iso-8859-16 is somehow broken in WebKit, at least on CI
|
|
28
|
+
return enc !== 'iso-8859-16'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function createSinglebyteDecoder(encoding, loose = false) {
|
|
32
|
+
assertEncoding(encoding)
|
|
33
|
+
|
|
34
|
+
if (canDecoders && shouldUseNative(encoding)) {
|
|
35
|
+
// In try, as not all encodings might be implemented in all engines which have native TextDecoder
|
|
36
|
+
try {
|
|
37
|
+
const decoder = new TextDecoder(encoding, { fatal: !loose })
|
|
38
|
+
return (arr) => {
|
|
39
|
+
assertUint8(arr)
|
|
40
|
+
if (arr.byteLength === 0) return ''
|
|
41
|
+
return decoder.decode(arr)
|
|
42
|
+
}
|
|
43
|
+
} catch {}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const jsDecoder = encodingDecoder(encoding)
|
|
47
|
+
return (arr) => {
|
|
48
|
+
assertUint8(arr)
|
|
49
|
+
if (arr.byteLength === 0) return ''
|
|
50
|
+
return jsDecoder(arr, loose)
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export const windows1252toString = createSinglebyteDecoder('windows-1252')
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { assertUint8 } from './assert.js'
|
|
2
|
+
import { isAscii } from 'node:buffer'
|
|
3
|
+
import { isDeno, isLE } from './fallback/_utils.js'
|
|
4
|
+
import { asciiPrefix } from './fallback/latin1.js'
|
|
5
|
+
import { encodingMapper, encodingDecoder, E_STRICT } from './fallback/single-byte.js'
|
|
6
|
+
|
|
7
|
+
const toBuf = (x) => Buffer.from(x.buffer, x.byteOffset, x.byteLength)
|
|
8
|
+
|
|
9
|
+
function latin1Prefix(arr, start) {
|
|
10
|
+
let p = start | 0
|
|
11
|
+
const length = arr.length
|
|
12
|
+
for (const len3 = length - 3; p < len3; p += 4) {
|
|
13
|
+
if ((arr[p] & 0xe0) === 0x80) return p
|
|
14
|
+
if ((arr[p + 1] & 0xe0) === 0x80) return p + 1
|
|
15
|
+
if ((arr[p + 2] & 0xe0) === 0x80) return p + 2
|
|
16
|
+
if ((arr[p + 3] & 0xe0) === 0x80) return p + 3
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
for (; p < length; p++) {
|
|
20
|
+
if ((arr[p] & 0xe0) === 0x80) return p
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return length
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function createSinglebyteDecoder(encoding, loose = false) {
|
|
27
|
+
const latin1path = encoding === 'windows-1252'
|
|
28
|
+
if (isDeno) {
|
|
29
|
+
const jsDecoder = encodingDecoder(encoding) // asserts
|
|
30
|
+
return (arr) => {
|
|
31
|
+
assertUint8(arr)
|
|
32
|
+
if (arr.byteLength === 0) return ''
|
|
33
|
+
if (isAscii(arr)) return toBuf(arr).toString()
|
|
34
|
+
return jsDecoder(arr, loose) // somewhy faster on Deno anyway, TODO: optimize?
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const { incomplete, mapper } = encodingMapper(encoding) // asserts
|
|
39
|
+
return (arr) => {
|
|
40
|
+
assertUint8(arr)
|
|
41
|
+
if (arr.byteLength === 0) return ''
|
|
42
|
+
if (isAscii(arr)) return toBuf(arr).latin1Slice(0, arr.byteLength) // .latin1Slice is faster than .asciiSlice
|
|
43
|
+
|
|
44
|
+
// Node.js TextDecoder is broken, so we can't use it. It's also slow anyway
|
|
45
|
+
|
|
46
|
+
let prefixBytes = asciiPrefix(arr)
|
|
47
|
+
let prefix = ''
|
|
48
|
+
if (latin1path) prefixBytes = latin1Prefix(arr, prefixBytes)
|
|
49
|
+
if (prefixBytes > 64 || prefixBytes === arr.length) {
|
|
50
|
+
prefix = toBuf(arr).latin1Slice(0, prefixBytes) // .latin1Slice is faster than .asciiSlice
|
|
51
|
+
if (prefixBytes === arr.length) return prefix
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const b = toBuf(mapper(arr, prefix.length)) // prefix.length can mismatch prefixBytes
|
|
55
|
+
if (!isLE) b.swap16()
|
|
56
|
+
const suffix = b.ucs2Slice(0, b.byteLength)
|
|
57
|
+
if (!loose && incomplete && suffix.includes('\uFFFD')) throw new TypeError(E_STRICT)
|
|
58
|
+
return prefix + suffix
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export const windows1252toString = createSinglebyteDecoder('windows-1252')
|