@exodus/bytes 1.12.0 → 1.13.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 CHANGED
@@ -30,7 +30,7 @@ Tested in CI with [@exodus/test](https://github.com/ExodusMovement/test#exoduste
30
30
  [![Hermes](https://img.shields.io/badge/Hermes-282C34?style=for-the-badge&logo=React)](https://hermesengine.dev)
31
31
  [![V8](https://img.shields.io/badge/V8-4285F4?style=for-the-badge&logo=V8&logoColor=white)](https://v8.dev/docs/d8)
32
32
  [![JavaScriptCore](https://img.shields.io/badge/JavaScriptCore-006CFF?style=for-the-badge)](https://docs.webkit.org/Deep%20Dive/JSC/JavaScriptCore.html)
33
- [![SpiderMonkey](https://img.shields.io/badge/SpiderMonkey-FFD681?style=for-the-badge)](https://spidermonkey.dev/)
33
+ [![SpiderMonkey](https://img.shields.io/badge/SpiderMonkey-FFD681?style=for-the-badge)](https://spidermonkey.dev/)\
34
34
  [![QuickJS](https://img.shields.io/badge/QuickJS-E58200?style=for-the-badge)](https://github.com/quickjs-ng/quickjs)
35
35
  [![XS](https://img.shields.io/badge/XS-0B307A?style=for-the-badge)](https://github.com/Moddable-OpenSource/moddable)
36
36
  [![GraalJS](https://img.shields.io/badge/GraalJS-C74634?style=for-the-badge)](https://github.com/oracle/graaljs)
@@ -100,24 +100,47 @@ _These are only provided as a compatibility layer, prefer hardened APIs instead
100
100
 
101
101
  ### Lite version
102
102
 
103
- If you don't need support for legacy multi-byte encodings, you can use the lite import:
104
- ```js
105
- import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-lite.js'
106
- import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-lite.js' // Requires Streams
107
- ```
103
+ Alternate exports exist that can help reduce bundle size, see comparison:
104
+
105
+ | import | size |
106
+ | - | - |
107
+ | [@exodus/bytes/encoding-browser.js](#exodusbytesencoding-browserjs-) | <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding-browser.js?style=flat-square)</sub> |
108
+ | [@exodus/bytes/encoding-lite.js](#exodusbytesencoding-litejs-) | <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding-lite.js?style=flat-square)</sub> |
109
+ | [@exodus/bytes/encoding.js](#exodusbytesencodingjs-) | <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding.js?style=flat-square)</sub> |
110
+ | `text-encoding` | <sub>![](https://img.shields.io/bundlejs/size/text-encoding?style=flat-square)</sub> |
111
+ | `iconv-lite` | <sub>![](https://img.shields.io/bundlejs/size/iconv-lite/lib/index.js?style=flat-square)</sub> |
112
+ | `whatwg-encoding` | <sub>![](https://img.shields.io/bundlejs/size/whatwg-encoding?style=flat-square)</sub> |
113
+
114
+ Libraries are advised to use single-purpose hardened `@exodus/bytes/utf8.js` / `@exodus/bytes/utf16.js` APIs for Unicode.
115
+
116
+ Applications (including React Native apps) are advised to load either `@exodus/bytes/encoding-lite.js` or `@exodus/bytes/encoding.js`
117
+ (depending on whether legacy multi-byte support is needed) and use that as a global polyfill.
108
118
 
109
- This reduces the bundle size 9x:\
110
- from 90 KiB gzipped for `@exodus/bytes/encoding.js` to 10 KiB gzipped for `@exodus/bytes/encoding-lite.js`.\
111
- (For comparison, `text-encoding` module is 190 KiB gzipped, and `iconv-lite` is 194 KiB gzipped):
119
+ #### `@exodus/bytes/encoding-lite.js`
112
120
 
113
- It still supports `utf-8`, `utf-16le`, `utf-16be` and all single-byte encodings specified by the spec,
114
- the only difference is support for legacy multi-byte encodings.
121
+ If you don't need support for legacy multi-byte encodings.
122
+
123
+ Reduces the bundle size 11x, while still keeping `utf-8`, `utf-16le`, `utf-16be` and all single-byte encodings specified by the spec.
124
+ The only difference is support for legacy multi-byte encodings.
115
125
 
116
126
  See [the list of encodings](https://encoding.spec.whatwg.org/#names-and-labels).
117
127
 
128
+ This can be useful for example in React Native global TextDecoder polyfill,
129
+ if you are sure that you don't need legacy multi-byte encodings support.
130
+
131
+ #### `@exodus/bytes/encoding-browser.js`
132
+
133
+ Resolves to a tiny import in browser bundles, preferring native `TextDecoder` / `TextEncoder`.
134
+
135
+ For non-browsers (Node.js, React Native), loads a full implementation.
136
+
137
+ > [!NOTE]
138
+ > This is not the default behavior for `@exodus/bytes/encoding.js` because all major browser implementations have bugs,
139
+ > which `@exodus/bytes/encoding.js` fixes. Only use if you are ok with that.
140
+
118
141
  ## API
119
142
 
120
- ### @exodus/bytes/utf8.js
143
+ ### @exodus/bytes/utf8.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/utf8.js?style=flat-square)<sub>
121
144
 
122
145
  UTF-8 encoding/decoding
123
146
 
@@ -183,7 +206,7 @@ Prefer using strict throwing methods for cryptography applications._
183
206
  This is similar to `new TextDecoder('utf-8', { ignoreBOM: true }).decode(arr)`,
184
207
  but works on all engines.
185
208
 
186
- ### @exodus/bytes/utf16.js
209
+ ### @exodus/bytes/utf16.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/utf16.js?style=flat-square)<sub>
187
210
 
188
211
  UTF-16 encoding/decoding
189
212
 
@@ -667,7 +690,7 @@ Create a view of a TypedArray in the specified format (`'uint8'` or `'buffer'`)
667
690
  > [!IMPORTANT]
668
691
  > Does not copy data, returns a view on the same underlying buffer
669
692
 
670
- ### @exodus/bytes/encoding.js
693
+ ### @exodus/bytes/encoding.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding.js?style=flat-square)</sub>
671
694
 
672
695
  Implements the [Encoding standard](https://encoding.spec.whatwg.org/):
673
696
  [TextDecoder](https://encoding.spec.whatwg.org/#interface-textdecoder),
@@ -778,7 +801,7 @@ only expects lowercased encoding name:
778
801
  new TextDecoder(getBOMEncoding(input) ?? fallbackEncoding).decode(input)
779
802
  ```
780
803
 
781
- ### @exodus/bytes/encoding-lite.js
804
+ ### @exodus/bytes/encoding-lite.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding-lite.js?style=flat-square)</sub>
782
805
 
783
806
  The exact same exports as `@exodus/bytes/encoding.js` are also exported as
784
807
  `@exodus/bytes/encoding-lite.js`, with the difference that the lite version does not load
@@ -837,7 +860,7 @@ true
837
860
  '%'
838
861
  ```
839
862
 
840
- ### @exodus/bytes/encoding-browser.js
863
+ ### @exodus/bytes/encoding-browser.js <sub>![](https://img.shields.io/bundlejs/size/@exodus/bytes/encoding-browser.js?style=flat-square)<sub>
841
864
 
842
865
  Same as `@exodus/bytes/encoding.js`, but in browsers instead of polyfilling just uses whatever the
843
866
  browser provides, drastically reducing the bundle size (to less than 2 KiB gzipped).
package/base58.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { typedView } from './array.js'
2
- import { assertUint8 } from './assert.js'
3
- import { nativeDecoder, nativeEncoder, isHermes, E_STRING } from './fallback/_utils.js'
2
+ import { assertU8, E_STRING } from './fallback/_utils.js'
3
+ import { nativeDecoder, nativeEncoder, isHermes } from './fallback/platform.js'
4
4
  import { encodeAscii, decodeAscii } from './fallback/latin1.js'
5
5
 
6
6
  const alphabet58 = [...'123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz']
@@ -23,7 +23,7 @@ const E_CHAR = 'Invalid character in base58 input'
23
23
  const shouldUseBigIntFrom = isHermes // faster only on Hermes, numbers path beats it on normal engines
24
24
 
25
25
  function toBase58core(arr, alphabet, codes) {
26
- assertUint8(arr)
26
+ assertU8(arr)
27
27
  const length = arr.length
28
28
  if (length === 0) return ''
29
29
 
package/base64.js CHANGED
@@ -1,6 +1,7 @@
1
- import { assertUint8, assertEmptyRest } from './assert.js'
1
+ import { assertEmptyRest } from './assert.js'
2
2
  import { typedView } from './array.js'
3
- import { isHermes, skipWeb, E_STRING } from './fallback/_utils.js'
3
+ import { assertU8, E_STRING } from './fallback/_utils.js'
4
+ import { isHermes } from './fallback/platform.js'
4
5
  import { decodeLatin1, encodeLatin1 } from './fallback/latin1.js'
5
6
  import * as js from './fallback/base64.js'
6
7
 
@@ -34,10 +35,10 @@ function maybePad(res, padding) {
34
35
  }
35
36
 
36
37
  const toUrl = (x) => x.replaceAll('+', '-').replaceAll('/', '_')
37
- const haveWeb = (x) => !skipWeb && web64 && x.toBase64 === web64
38
+ const haveWeb = (x) => web64 && x.toBase64 === web64
38
39
 
39
40
  export function toBase64(x, { padding = true } = {}) {
40
- assertUint8(x)
41
+ assertU8(x)
41
42
  if (haveWeb(x)) return padding ? x.toBase64() : x.toBase64({ omitPadding: !padding }) // Modern, optionless is slightly faster
42
43
  if (haveNativeBuffer) return maybeUnpad(toBuffer(x).base64Slice(0, x.byteLength), padding) // Older Node.js
43
44
  if (shouldUseBtoa) return maybeUnpad(btoa(decodeLatin1(x)), padding)
@@ -46,7 +47,7 @@ export function toBase64(x, { padding = true } = {}) {
46
47
 
47
48
  // NOTE: base64url omits padding by default
48
49
  export function toBase64url(x, { padding = false } = {}) {
49
- assertUint8(x)
50
+ assertU8(x)
50
51
  if (haveWeb(x)) return x.toBase64({ alphabet: 'base64url', omitPadding: !padding }) // Modern
51
52
  if (haveNativeBuffer) return maybePad(toBuffer(x).base64urlSlice(0, x.byteLength), padding) // Older Node.js
52
53
  if (shouldUseBtoa) return maybeUnpad(toUrl(btoa(decodeLatin1(x))), padding)
@@ -111,7 +112,7 @@ function noWhitespaceSeen(str, arr) {
111
112
  }
112
113
 
113
114
  let fromBase64impl
114
- if (!skipWeb && Uint8Array.fromBase64) {
115
+ if (Uint8Array.fromBase64) {
115
116
  // NOTICE: this is actually slower than our JS impl in older JavaScriptCore and (slightly) in SpiderMonkey, but faster on V8 and new JavaScriptCore
116
117
  fromBase64impl = (str, isBase64url, padding) => {
117
118
  const alphabet = isBase64url ? 'base64url' : 'base64'
package/bech32.js CHANGED
@@ -1,5 +1,5 @@
1
- import { assertUint8 } from './assert.js'
2
- import { nativeEncoder, E_STRING } from './fallback/_utils.js'
1
+ import { assertU8, E_STRING } from './fallback/_utils.js'
2
+ import { nativeEncoder } from './fallback/platform.js'
3
3
  import { decodeAscii, encodeAscii, encodeLatin1 } from './fallback/latin1.js'
4
4
 
5
5
  const alphabet = [...'qpzry9x8gf2tvdw0s3jn54khce6mua7l']
@@ -109,7 +109,7 @@ function pPrefix(prefix) {
109
109
  function toBech32enc(prefix, bytes, limit, encoding) {
110
110
  if (typeof prefix !== 'string' || !prefix) throw new TypeError(E_PREFIX)
111
111
  if (typeof limit !== 'number') throw new TypeError(E_SIZE)
112
- assertUint8(bytes)
112
+ assertU8(bytes)
113
113
  const bytesLength = bytes.length
114
114
  const wordsLength = Math.ceil((bytesLength * 8) / 5)
115
115
  if (!(prefix.length + 7 + wordsLength <= limit)) throw new TypeError(E_SIZE)
@@ -1,10 +1,4 @@
1
- import {
2
- fromSource,
3
- getBOMEncoding,
4
- normalizeEncoding,
5
- E_ENCODING,
6
- } from './fallback/encoding.api.js'
7
- import labels from './fallback/encoding.labels.js'
1
+ import { getBOMEncoding } from './fallback/encoding.api.js'
8
2
 
9
3
  // Lite-weight version which re-exports existing implementations on browsers,
10
4
  // while still being aliased to the full impl in RN and Node.js
@@ -13,17 +7,49 @@ import labels from './fallback/encoding.labels.js'
13
7
 
14
8
  const { TextDecoder, TextEncoder, TextDecoderStream, TextEncoderStream } = globalThis
15
9
 
16
- export { normalizeEncoding, getBOMEncoding, labelToName } from './fallback/encoding.api.js'
10
+ export { getBOMEncoding } from './fallback/encoding.api.js'
17
11
  export { TextDecoder, TextEncoder, TextDecoderStream, TextEncoderStream }
18
12
 
19
- // https://encoding.spec.whatwg.org/#decode
13
+ export function normalizeEncoding(label) {
14
+ if (label === 'utf-8' || label === 'utf8' || label === 'UTF-8' || label === 'UTF8') return 'utf-8'
15
+ if (label === 'windows-1252' || label === 'ascii' || label === 'latin1') return 'windows-1252'
16
+ if (/[^\w\t\n\f\r .:-]/i.test(label)) return null
17
+ const l = `${label}`.trim().toLowerCase()
18
+ try {
19
+ return new TextDecoder(l).encoding
20
+ } catch {}
21
+
22
+ if (l === 'x-user-defined') return l
23
+ if (
24
+ l === 'replacement' ||
25
+ l === 'csiso2022kr' ||
26
+ l === 'hz-gb-2312' ||
27
+ l === 'iso-2022-cn' ||
28
+ l === 'iso-2022-cn-ext' ||
29
+ l === 'iso-2022-kr'
30
+ ) {
31
+ return 'replacement'
32
+ }
33
+
34
+ return null
35
+ }
36
+
20
37
  export function legacyHookDecode(input, fallbackEncoding = 'utf-8') {
21
- let u8 = fromSource(input)
22
- const bomEncoding = getBOMEncoding(u8)
23
- if (bomEncoding) u8 = u8.subarray(bomEncoding === 'utf-8' ? 3 : 2)
24
- const enc = bomEncoding ?? normalizeEncoding(fallbackEncoding) // "the byte order mark is more authoritative than anything else"
25
- if (enc === 'utf-8') return new TextDecoder('utf-8', { ignoreBOM: true }).decode(u8) // fast path
26
- if (enc === 'replacement') return u8.byteLength > 0 ? '\uFFFD' : ''
27
- if (!Object.hasOwn(labels, enc)) throw new RangeError(E_ENCODING)
28
- return new TextDecoder(enc, { ignoreBOM: true }).decode(u8)
38
+ const enc = getBOMEncoding(input) ?? normalizeEncoding(fallbackEncoding)
39
+ if (enc === 'replacement') return input.byteLength > 0 ? '\uFFFD' : ''
40
+ return new TextDecoder(enc).decode(input)
41
+ }
42
+
43
+ export function labelToName(label) {
44
+ const enc = normalizeEncoding(label)
45
+ if (enc === 'utf-8') return 'UTF-8'
46
+ if (!enc) return enc
47
+ const p = enc.slice(0, 3)
48
+ if (p === 'utf' || p === 'iso' || p === 'koi' || p === 'euc' || p === 'ibm' || p === 'gbk') {
49
+ return enc.toUpperCase()
50
+ }
51
+
52
+ if (enc === 'big5') return 'Big5'
53
+ if (enc === 'shift_jis') return 'Shift_JIS'
54
+ return enc
29
55
  }
@@ -1,131 +1,15 @@
1
- const { Buffer, TextEncoder, TextDecoder } = globalThis
2
- const haveNativeBuffer = Buffer && !Buffer.TYPED_ARRAY_SUPPORT
3
- export const nativeBuffer = haveNativeBuffer ? Buffer : null
4
- export const isHermes = !!globalThis.HermesInternal
5
- export const isDeno = !!globalThis.Deno
6
- export const isLE = /* @__PURE__ */ (() => new Uint8Array(Uint16Array.of(258).buffer)[0] === 2)()
1
+ export * from './platform.js'
7
2
 
8
- // We consider Node.js TextDecoder/TextEncoder native
9
- let isNative = (x) => x && (haveNativeBuffer || `${x}`.includes('[native code]'))
10
- if (!haveNativeBuffer && isNative(() => {})) isNative = () => false // e.g. XS, we don't want false positives
11
-
12
- export const nativeEncoder = isNative(TextEncoder) ? new TextEncoder() : null
13
- export const nativeDecoder = isNative(TextDecoder)
14
- ? new TextDecoder('utf-8', { ignoreBOM: true })
15
- : null
16
-
17
- // Actually windows-1252, compatible with ascii and latin1 decoding
18
- // Beware that on non-latin1, i.e. on windows-1252, this is broken in ~all Node.js versions released
19
- // in 2025 due to a regression, so we call it Latin1 as it's usable only for that
20
- const getNativeLatin1 = () => {
21
- // Not all barebone engines with TextDecoder support something except utf-8, detect
22
- if (nativeDecoder) {
23
- try {
24
- return new TextDecoder('latin1', { ignoreBOM: true })
25
- } catch {}
26
- }
27
-
28
- return null
29
- }
30
-
31
- export const nativeDecoderLatin1 = /* @__PURE__ */ getNativeLatin1()
32
-
33
- // Block Firefox < 146 specifically from using native hex/base64, as it's very slow there
34
- // Refs: https://bugzilla.mozilla.org/show_bug.cgi?id=1994067 (and linked issues), fixed in 146
35
- // Before that, all versions of Firefox >= 133 are slow
36
- // TODO: this could be removed when < 146 usage diminishes (note ESR)
37
- // We do not worry about false-negatives here but worry about false-positives!
38
- function shouldSkipBuiltins() {
39
- const g = globalThis
40
- // First, attempt to exclude as many things as we can using trivial checks, just in case, and to not hit ua
41
- if (haveNativeBuffer || isHermes || !g.window || g.chrome || !g.navigator) return false
42
- try {
43
- // This was fixed specifically in Firefox 146. Other engines except Hermes (already returned) get this right
44
- new WeakSet().add(Symbol()) // eslint-disable-line symbol-description
45
- return false
46
- } catch {
47
- // In catch and not after in case if something too smart optimizes out code in try. False-negative is acceptable in that case
48
- if (!('onmozfullscreenerror' in g)) return false // Firefox has it (might remove in the future, but we don't care)
49
- return /firefox/i.test(g.navigator.userAgent || '') // as simple as we can
50
- }
51
-
52
- /* c8 ignore next */
53
- return false // eslint-disable-line no-unreachable
54
- }
55
-
56
- export const skipWeb = /* @__PURE__ */ shouldSkipBuiltins()
57
-
58
- function decodePartAddition(a, start, end, m) {
59
- let o = ''
60
- let i = start
61
- for (const last3 = end - 3; i < last3; i += 4) {
62
- const x0 = a[i]
63
- const x1 = a[i + 1]
64
- const x2 = a[i + 2]
65
- const x3 = a[i + 3]
66
- o += m[x0]
67
- o += m[x1]
68
- o += m[x2]
69
- o += m[x3]
70
- }
71
-
72
- while (i < end) o += m[a[i++]]
73
- return o
74
- }
75
-
76
- // Decoding with templates is faster on Hermes
77
- function decodePartTemplates(a, start, end, m) {
78
- let o = ''
79
- let i = start
80
- for (const last15 = end - 15; i < last15; i += 16) {
81
- const x0 = a[i]
82
- const x1 = a[i + 1]
83
- const x2 = a[i + 2]
84
- const x3 = a[i + 3]
85
- const x4 = a[i + 4]
86
- const x5 = a[i + 5]
87
- const x6 = a[i + 6]
88
- const x7 = a[i + 7]
89
- const x8 = a[i + 8]
90
- const x9 = a[i + 9]
91
- const x10 = a[i + 10]
92
- const x11 = a[i + 11]
93
- const x12 = a[i + 12]
94
- const x13 = a[i + 13]
95
- const x14 = a[i + 14]
96
- const x15 = a[i + 15]
97
- o += `${m[x0]}${m[x1]}${m[x2]}${m[x3]}${m[x4]}${m[x5]}${m[x6]}${m[x7]}${m[x8]}${m[x9]}${m[x10]}${m[x11]}${m[x12]}${m[x13]}${m[x14]}${m[x15]}`
98
- }
99
-
100
- while (i < end) o += m[a[i++]]
101
- return o
102
- }
103
-
104
- const decodePart = isHermes ? decodePartTemplates : decodePartAddition
105
- export function decode2string(arr, start, end, m) {
106
- if (end - start > 30_000) {
107
- // Limit concatenation to avoid excessive GC
108
- // Thresholds checked on Hermes for toHex
109
- const concat = []
110
- for (let i = start; i < end; ) {
111
- const step = i + 500
112
- const iNext = step > end ? end : step
113
- concat.push(decodePart(arr, i, iNext, m))
114
- i = iNext
115
- }
116
-
117
- const res = concat.join('')
118
- concat.length = 0
119
- return res
120
- }
121
-
122
- return decodePart(arr, start, end, m)
123
- }
3
+ const { Buffer } = globalThis
124
4
 
125
5
  export function assert(condition, msg) {
126
6
  if (!condition) throw new Error(msg)
127
7
  }
128
8
 
9
+ export function assertU8(arg) {
10
+ if (!(arg instanceof Uint8Array)) throw new TypeError('Expected an Uint8Array')
11
+ }
12
+
129
13
  // On arrays in heap (<= 64) it's cheaper to copy into a pooled buffer than lazy-create the ArrayBuffer storage
130
14
  export const toBuf = (x) =>
131
15
  x.byteLength <= 64 && x.BYTES_PER_ELEMENT === 1
@@ -133,3 +17,4 @@ export const toBuf = (x) =>
133
17
  : Buffer.from(x.buffer, x.byteOffset, x.byteLength)
134
18
 
135
19
  export const E_STRING = 'Input is not a string'
20
+ export const E_STRICT_UNICODE = 'Input is not well-formed Unicode'
@@ -1,5 +1,5 @@
1
- import { assertUint8 } from '../assert.js'
2
- import { nativeEncoder, nativeDecoder, isHermes } from './_utils.js'
1
+ import { assertU8 } from './_utils.js'
2
+ import { nativeEncoder, nativeDecoder, isHermes } from './platform.js'
3
3
  import { encodeAscii, decodeAscii } from './latin1.js'
4
4
 
5
5
  // See https://datatracker.ietf.org/doc/html/rfc4648
@@ -18,7 +18,7 @@ const useTemplates = isHermes // Faster on Hermes and JSC, but we use it only on
18
18
 
19
19
  // We construct output by concatenating chars, this seems to be fine enough on modern JS engines
20
20
  export function toBase32(arr, isBase32Hex, padding) {
21
- assertUint8(arr)
21
+ assertU8(arr)
22
22
  const fullChunks = Math.floor(arr.length / 5)
23
23
  const fullChunksBytes = fullChunks * 5
24
24
  let o = ''
@@ -1,6 +1,6 @@
1
1
  import { typedView } from '@exodus/bytes/array.js'
2
2
  import { toBase58, fromBase58 } from '@exodus/bytes/base58.js'
3
- import { assertUint8 } from '../assert.js'
3
+ import { assertU8 } from './_utils.js'
4
4
 
5
5
  const E_CHECKSUM = 'Invalid checksum'
6
6
 
@@ -28,7 +28,7 @@ function assertChecksum(c, r) {
28
28
  export const makeBase58check = (hashAlgo, hashAlgoSync) => {
29
29
  const apis = {
30
30
  async encode(arr) {
31
- assertUint8(arr)
31
+ assertU8(arr)
32
32
  return encodeWithChecksum(arr, await hashAlgo(arr))
33
33
  },
34
34
  async decode(str, format = 'uint8') {
@@ -41,7 +41,7 @@ export const makeBase58check = (hashAlgo, hashAlgoSync) => {
41
41
  return {
42
42
  ...apis,
43
43
  encodeSync(arr) {
44
- assertUint8(arr)
44
+ assertU8(arr)
45
45
  return encodeWithChecksum(arr, hashAlgoSync(arr))
46
46
  },
47
47
  decodeSync(str, format = 'uint8') {
@@ -1,5 +1,4 @@
1
- import { assertUint8 } from '../assert.js'
2
- import { nativeEncoder, nativeDecoder } from './_utils.js'
1
+ import { nativeEncoder, nativeDecoder } from './platform.js'
3
2
  import { encodeAscii, decodeAscii } from './latin1.js'
4
3
 
5
4
  // See https://datatracker.ietf.org/doc/html/rfc4648
@@ -15,8 +14,8 @@ export const E_LENGTH = 'Invalid base64 length'
15
14
  export const E_LAST = 'Invalid last chunk'
16
15
 
17
16
  // We construct output by concatenating chars, this seems to be fine enough on modern JS engines
17
+ // Expects a checked Uint8Array
18
18
  export function toBase64(arr, isURL, padding) {
19
- assertUint8(arr)
20
19
  const fullChunks = (arr.length / 3) | 0
21
20
  const fullChunksBytes = fullChunks * 3
22
21
  let o = ''
@@ -1,32 +1,3 @@
1
- import labels from './encoding.labels.js'
2
-
3
- let labelsMap
4
-
5
- export const E_ENCODING = 'Unknown encoding'
6
-
7
- // Warning: unlike whatwg-encoding, returns lowercased labels
8
- // Those are case-insensitive and that's how TextDecoder encoding getter normalizes them
9
- // https://encoding.spec.whatwg.org/#names-and-labels
10
- export function normalizeEncoding(label) {
11
- // fast path
12
- if (label === 'utf-8' || label === 'utf8' || label === 'UTF-8' || label === 'UTF8') return 'utf-8'
13
- if (label === 'windows-1252' || label === 'ascii' || label === 'latin1') return 'windows-1252'
14
- // full map
15
- if (/[^\w\t\n\f\r .:-]/i.test(label)) return null // must be ASCII (with ASCII whitespace)
16
- const low = `${label}`.trim().toLowerCase()
17
- if (Object.hasOwn(labels, low)) return low
18
- if (!labelsMap) {
19
- labelsMap = new Map()
20
- for (const [label, aliases] of Object.entries(labels)) {
21
- for (const alias of aliases) labelsMap.set(alias, label)
22
- }
23
- }
24
-
25
- const mapped = labelsMap.get(low)
26
- if (mapped) return mapped
27
- return null
28
- }
29
-
30
1
  // TODO: make this more strict against Symbol.toStringTag
31
2
  // Is not very significant though, anything faking Symbol.toStringTag could as well override
32
3
  // prototypes, which is not something we protect against
@@ -65,17 +36,3 @@ export function getBOMEncoding(input) {
65
36
  if (u8[0] === 0xfe && u8[1] === 0xff) return 'utf-16be'
66
37
  return null
67
38
  }
68
-
69
- const uppercasePrefixes = new Set(['utf', 'iso', 'koi', 'euc', 'ibm', 'gbk'])
70
-
71
- // Unlike normalizeEncoding, case-sensitive
72
- // https://encoding.spec.whatwg.org/#names-and-labels
73
- export function labelToName(label) {
74
- const enc = normalizeEncoding(label)
75
- if (enc === 'utf-8') return 'UTF-8' // fast path
76
- if (!enc) return enc
77
- if (uppercasePrefixes.has(enc.slice(0, 3))) return enc.toUpperCase()
78
- if (enc === 'big5') return 'Big5'
79
- if (enc === 'shift_jis') return 'Shift_JIS'
80
- return enc
81
- }
@@ -5,17 +5,56 @@ import { utf16toString, utf16toStringLoose } from '@exodus/bytes/utf16.js'
5
5
  import { utf8fromStringLoose, utf8toString, utf8toStringLoose } from '@exodus/bytes/utf8.js'
6
6
  import { createSinglebyteDecoder } from '@exodus/bytes/single-byte.js'
7
7
  import labels from './encoding.labels.js'
8
- import { fromSource, getBOMEncoding, normalizeEncoding, E_ENCODING } from './encoding.api.js'
8
+ import { fromSource, getBOMEncoding } from './encoding.api.js'
9
9
  import { unfinishedBytes, mergePrefix } from './encoding.util.js'
10
10
 
11
- export { labelToName, getBOMEncoding, normalizeEncoding } from './encoding.api.js'
11
+ export { getBOMEncoding } from './encoding.api.js'
12
12
 
13
+ export const E_ENCODING = 'Unknown encoding'
13
14
  const E_MULTI = "import '@exodus/bytes/encoding.js' for legacy multi-byte encodings support"
14
15
  const E_OPTIONS = 'The "options" argument must be of type object'
15
16
  const replacementChar = '\uFFFD'
16
17
  const multibyteSet = new Set(['big5', 'euc-kr', 'euc-jp', 'iso-2022-jp', 'shift_jis', 'gbk', 'gb18030']) // prettier-ignore
17
18
  let createMultibyteDecoder, multibyteEncoder
18
19
 
20
+ let labelsMap
21
+ // Warning: unlike whatwg-encoding, returns lowercased labels
22
+ // Those are case-insensitive and that's how TextDecoder encoding getter normalizes them
23
+ // https://encoding.spec.whatwg.org/#names-and-labels
24
+ export function normalizeEncoding(label) {
25
+ // fast path
26
+ if (label === 'utf-8' || label === 'utf8' || label === 'UTF-8' || label === 'UTF8') return 'utf-8'
27
+ if (label === 'windows-1252' || label === 'ascii' || label === 'latin1') return 'windows-1252'
28
+ // full map
29
+ if (/[^\w\t\n\f\r .:-]/i.test(label)) return null // must be ASCII (with ASCII whitespace)
30
+ const low = `${label}`.trim().toLowerCase()
31
+ if (Object.hasOwn(labels, low)) return low
32
+ if (!labelsMap) {
33
+ labelsMap = new Map()
34
+ for (const [name, aliases] of Object.entries(labels)) {
35
+ for (const alias of aliases) labelsMap.set(alias, name)
36
+ }
37
+ }
38
+
39
+ const mapped = labelsMap.get(low)
40
+ if (mapped) return mapped
41
+ return null
42
+ }
43
+
44
+ const uppercasePrefixes = new Set(['utf', 'iso', 'koi', 'euc', 'ibm', 'gbk'])
45
+
46
+ // Unlike normalizeEncoding, case-sensitive
47
+ // https://encoding.spec.whatwg.org/#names-and-labels
48
+ export function labelToName(label) {
49
+ const enc = normalizeEncoding(label)
50
+ if (enc === 'utf-8') return 'UTF-8' // fast path
51
+ if (!enc) return enc
52
+ if (uppercasePrefixes.has(enc.slice(0, 3))) return enc.toUpperCase()
53
+ if (enc === 'big5') return 'Big5'
54
+ if (enc === 'shift_jis') return 'Shift_JIS'
55
+ return enc
56
+ }
57
+
19
58
  export const isMultibyte = (enc) => multibyteSet.has(enc)
20
59
  export function setMultibyte(createDecoder, createEncoder) {
21
60
  createMultibyteDecoder = createDecoder
@@ -4,43 +4,47 @@
4
4
  // prettier-ignore
5
5
  const labels = {
6
6
  'utf-8': ['unicode-1-1-utf-8', 'unicode11utf8', 'unicode20utf8', 'utf8', 'x-unicode20utf8'],
7
- ibm866: ['866', 'cp866', 'csibm866'],
8
- 'iso-8859-2': ['csisolatin2', 'iso-ir-101', 'iso8859-2', 'iso88592', 'iso_8859-2', 'iso_8859-2:1987', 'l2', 'latin2'],
9
- 'iso-8859-3': ['csisolatin3', 'iso-ir-109', 'iso8859-3', 'iso88593', 'iso_8859-3', 'iso_8859-3:1988', 'l3', 'latin3'],
10
- 'iso-8859-4': ['csisolatin4', 'iso-ir-110', 'iso8859-4', 'iso88594', 'iso_8859-4', 'iso_8859-4:1988', 'l4', 'latin4'],
11
- 'iso-8859-5': ['csisolatincyrillic', 'cyrillic', 'iso-ir-144', 'iso8859-5', 'iso88595', 'iso_8859-5', 'iso_8859-5:1988'],
12
- 'iso-8859-6': ['arabic', 'asmo-708', 'csiso88596e', 'csiso88596i', 'csisolatinarabic', 'ecma-114', 'iso-8859-6-e', 'iso-8859-6-i', 'iso-ir-127', 'iso8859-6', 'iso88596', 'iso_8859-6', 'iso_8859-6:1987'],
13
- 'iso-8859-7': ['csisolatingreek', 'ecma-118', 'elot_928', 'greek', 'greek8', 'iso-ir-126', 'iso8859-7', 'iso88597', 'iso_8859-7', 'iso_8859-7:1987', 'sun_eu_greek'],
14
- 'iso-8859-8': ['csiso88598e', 'csisolatinhebrew', 'hebrew', 'iso-8859-8-e', 'iso-ir-138', 'iso8859-8', 'iso88598', 'iso_8859-8', 'iso_8859-8:1988', 'visual'],
7
+ 'utf-16be': ['unicodefffe'],
8
+ 'utf-16le': ['csunicode', 'iso-10646-ucs-2', 'ucs-2', 'unicode', 'unicodefeff', 'utf-16'],
9
+ 'iso-8859-2': ['iso-ir-101'],
10
+ 'iso-8859-3': ['iso-ir-109'],
11
+ 'iso-8859-4': ['iso-ir-110'],
12
+ 'iso-8859-5': ['csisolatincyrillic', 'cyrillic', 'iso-ir-144'],
13
+ 'iso-8859-6': ['arabic', 'asmo-708', 'csiso88596e', 'csiso88596i', 'csisolatinarabic', 'ecma-114', 'iso-8859-6-e', 'iso-8859-6-i', 'iso-ir-127'],
14
+ 'iso-8859-7': ['csisolatingreek', 'ecma-118', 'elot_928', 'greek', 'greek8', 'iso-ir-126', 'sun_eu_greek'],
15
+ 'iso-8859-8': ['csiso88598e', 'csisolatinhebrew', 'hebrew', 'iso-8859-8-e', 'iso-ir-138', 'visual'],
15
16
  'iso-8859-8-i': ['csiso88598i', 'logical'],
16
- 'iso-8859-10': ['csisolatin6', 'iso-ir-157', 'iso8859-10', 'iso885910', 'l6', 'latin6'],
17
- 'iso-8859-13': ['iso8859-13', 'iso885913'],
18
- 'iso-8859-14': ['iso8859-14', 'iso885914'],
19
- 'iso-8859-15': ['csisolatin9', 'iso8859-15', 'iso885915', 'iso_8859-15', 'l9'],
20
17
  'iso-8859-16': [],
21
18
  'koi8-r': ['cskoi8r', 'koi', 'koi8', 'koi8_r'],
22
19
  'koi8-u': ['koi8-ru'],
23
- macintosh: ['csmacintosh', 'mac', 'x-mac-roman'],
24
20
  'windows-874': ['dos-874', 'iso-8859-11', 'iso8859-11', 'iso885911', 'tis-620'],
21
+ ibm866: ['866', 'cp866', 'csibm866'],
25
22
  'x-mac-cyrillic': ['x-mac-ukrainian'],
23
+ macintosh: ['csmacintosh', 'mac', 'x-mac-roman'],
26
24
  gbk: ['chinese', 'csgb2312', 'csiso58gb231280', 'gb2312', 'gb_2312', 'gb_2312-80', 'iso-ir-58', 'x-gbk'],
27
25
  gb18030: [],
28
26
  big5: ['big5-hkscs', 'cn-big5', 'csbig5', 'x-x-big5'],
29
27
  'euc-jp': ['cseucpkdfmtjapanese', 'x-euc-jp'],
30
- 'iso-2022-jp': ['csiso2022jp'],
31
28
  shift_jis: ['csshiftjis', 'ms932', 'ms_kanji', 'shift-jis', 'sjis', 'windows-31j', 'x-sjis'],
32
29
  'euc-kr': ['cseuckr', 'csksc56011987', 'iso-ir-149', 'korean', 'ks_c_5601-1987', 'ks_c_5601-1989', 'ksc5601', 'ksc_5601', 'windows-949'],
30
+ 'iso-2022-jp': ['csiso2022jp'],
33
31
  replacement: ['csiso2022kr', 'hz-gb-2312', 'iso-2022-cn', 'iso-2022-cn-ext', 'iso-2022-kr'],
34
- 'utf-16be': ['unicodefffe'],
35
- 'utf-16le': ['csunicode', 'iso-10646-ucs-2', 'ucs-2', 'unicode', 'unicodefeff', 'utf-16'],
36
32
  'x-user-defined': [],
37
33
  }
38
34
 
35
+ for (const i of [10, 13, 14, 15]) labels[`iso-8859-${i}`] = [`iso8859-${i}`, `iso8859${i}`]
36
+ for (const i of [2, 6, 7]) labels[`iso-8859-${i}`].push(`iso_8859-${i}:1987`)
37
+ for (const i of [3, 4, 5, 8]) labels[`iso-8859-${i}`].push(`iso_8859-${i}:1988`)
38
+ // prettier-ignore
39
+ for (let i = 2; i < 9; i++) labels[`iso-8859-${i}`].push(`iso8859-${i}`, `iso8859${i}`, `iso_8859-${i}`)
40
+ for (let i = 2; i < 5; i++) labels[`iso-8859-${i}`].push(`csisolatin${i}`, `l${i}`, `latin${i}`)
39
41
  for (let i = 0; i < 9; i++) labels[`windows-125${i}`] = [`cp125${i}`, `x-cp125${i}`]
40
42
 
41
43
  // prettier-ignore
42
44
  labels['windows-1252'].push('ansi_x3.4-1968', 'ascii', 'cp819', 'csisolatin1', 'ibm819', 'iso-8859-1', 'iso-ir-100', 'iso8859-1', 'iso88591', 'iso_8859-1', 'iso_8859-1:1987', 'l1', 'latin1', 'us-ascii')
43
45
  // prettier-ignore
44
46
  labels['windows-1254'].push('csisolatin5', 'iso-8859-9', 'iso-ir-148', 'iso8859-9', 'iso88599', 'iso_8859-9', 'iso_8859-9:1989', 'l5', 'latin5')
47
+ labels['iso-8859-10'].push('csisolatin6', 'iso-ir-157', 'l6', 'latin6')
48
+ labels['iso-8859-15'].push('csisolatin9', 'iso_8859-15', 'l9')
45
49
 
46
50
  export default labels