Skip to content

Commit 593fe7e

Browse files
authored
feat: isomorphic encode/decode (#68)
* feat: isomorphic encode/decode * test: add isomorphic tests
1 parent a0b18ee commit 593fe7e

File tree

11 files changed

+162
-4
lines changed

11 files changed

+162
-4
lines changed

README.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,9 @@ const latin1toString = createSinglebyteDecoder('iso-8859-1')
344344
> This is different from `new TextDecoder('iso-8859-1')` and `new TextDecoder('latin1')`, as those
345345
> alias to `new TextDecoder('windows-1252')`.
346346
347+
Prefer using `isomorphicDecode()` from `@exodus/bytes/encoding.js` or `@exodus/bytes/encoding-lite.js`,
348+
which is identical to this but allows more input types.
349+
347350
#### `latin1fromString(string)`
348351

349352
Encode a string to `iso-8859-1` bytes.
@@ -355,6 +358,8 @@ Same as:
355358
const latin1fromString = createSinglebyteEncoder('iso-8859-1', { mode: 'fatal' })
356359
```
357360

361+
Prefer using `isomorphicEncode()` from `@exodus/bytes/encoding.js` or `@exodus/bytes/encoding-lite.js`.
362+
358363
#### `windows1252toString(arr)`
359364

360365
Decode `windows-1252` bytes to a string.
@@ -714,6 +719,7 @@ some [hooks](https://encoding.spec.whatwg.org/#specification-hooks).
714719
```js
715720
import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding.js'
716721
import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding.js' // Requires Streams
722+
import { isomorphicDecode, isomorphicEncode } from '@exodus/bytes/encoding.js'
717723

718724
// Hooks for standards
719725
import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding.js'
@@ -749,6 +755,26 @@ A [Streams](https://streams.spec.whatwg.org/) wrapper for `TextEncoder`.
749755
Requires [Streams](https://streams.spec.whatwg.org/) to be either supported by the platform or
750756
[polyfilled](https://npmjs.com/package/web-streams-polyfill).
751757

758+
#### `isomorphicDecode(input)`
759+
760+
Implements [isomorphic decode](https://infra.spec.whatwg.org/#isomorphic-decode).
761+
762+
Given a `TypedArray` or an `ArrayBuffer` instance `input`, creates a string of the same length
763+
as input byteLength, using bytes from input as codepoints.
764+
765+
E.g. for `Uint8Array` input, this is similar to `String.fromCodePoint(...input)`.
766+
767+
Wider `TypedArray` inputs, e.g. `Uint16Array`, are interpreted as underlying _bytes_.
768+
769+
#### `isomorphicEncode(str)`
770+
771+
Implements [isomorphic encode](https://infra.spec.whatwg.org/#isomorphic-encode).
772+
773+
Given a string, creates an `Uint8Array` of the same length with the string codepoints as byte values.
774+
775+
Accepts only [isomorphic string](https://infra.spec.whatwg.org/#isomorphic-string) input
776+
and asserts that, throwing on any strings containing codepoints higher than `U+00FF`.
777+
752778
#### `labelToName(label)`
753779

754780
Implements [get an encoding from a string `label`](https://encoding.spec.whatwg.org/#concept-encoding-get).
@@ -822,6 +848,7 @@ multi-byte `TextDecoder` encodings by default to reduce bundle size ~12x.
822848
```js
823849
import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-lite.js'
824850
import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-lite.js' // Requires Streams
851+
import { isomorphicDecode, isomorphicEncode } from '@exodus/bytes/encoding-lite.js'
825852

826853
// Hooks for standards
827854
import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding-lite.js'
@@ -877,6 +904,8 @@ true
877904
Same as `@exodus/bytes/encoding.js`, but in browsers instead of polyfilling just uses whatever the
878905
browser provides, drastically reducing the bundle size (to less than 2 KiB gzipped).
879906
907+
Does not provide `isomorphicDecode` and `isomorphicEncode` exports.
908+
880909
```js
881910
import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-browser.js'
882911
import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-browser.js' // Requires Streams

encoding-browser.d.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* Same as `@exodus/bytes/encoding.js`, but in browsers instead of polyfilling just uses whatever the
33
* browser provides, drastically reducing the bundle size (to less than 2 KiB gzipped).
44
*
5+
* Does not provide `isomorphicDecode` and `isomorphicEncode` exports.
6+
*
57
* ```js
68
* import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-browser.js'
79
* import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-browser.js' // Requires Streams
@@ -21,4 +23,13 @@
2123
* @module @exodus/bytes/encoding-browser.js
2224
*/
2325

24-
export * from './encoding.js'
26+
export {
27+
TextDecoder,
28+
TextEncoder,
29+
TextDecoderStream,
30+
TextEncoderStream,
31+
normalizeEncoding,
32+
getBOMEncoding,
33+
labelToName,
34+
legacyHookDecode,
35+
} from './encoding.js'

encoding-browser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
export * from './encoding.js'
1+
export * from './encoding-browser.native.js'

encoding-browser.native.js

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,10 @@
1-
export * from './encoding.js'
1+
export {
2+
TextDecoder,
3+
TextEncoder,
4+
TextDecoderStream,
5+
TextEncoderStream,
6+
normalizeEncoding,
7+
getBOMEncoding,
8+
labelToName,
9+
legacyHookDecode,
10+
} from './encoding.js'

encoding-lite.d.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
* ```js
77
* import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding-lite.js'
88
* import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding-lite.js' // Requires Streams
9+
* import { isomorphicDecode, isomorphicEncode } from '@exodus/bytes/encoding-lite.js'
910
*
1011
* // Hooks for standards
1112
* import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding-lite.js'

encoding-lite.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,6 @@ export {
77
getBOMEncoding,
88
labelToName,
99
legacyHookDecode,
10+
isomorphicDecode,
11+
isomorphicEncode,
1012
} from './fallback/encoding.js'

encoding.d.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
* ```js
1010
* import { TextDecoder, TextEncoder } from '@exodus/bytes/encoding.js'
1111
* import { TextDecoderStream, TextEncoderStream } from '@exodus/bytes/encoding.js' // Requires Streams
12+
* import { isomorphicDecode, isomorphicEncode } from '@exodus/bytes/encoding.js'
1213
*
1314
* // Hooks for standards
1415
* import { getBOMEncoding, legacyHookDecode, labelToName, normalizeEncoding } from '@exodus/bytes/encoding.js'
@@ -90,6 +91,34 @@ export function legacyHookDecode(
9091
fallbackEncoding?: string
9192
): string;
9293

94+
/**
95+
* Implements [isomorphic decode](https://infra.spec.whatwg.org/#isomorphic-decode).
96+
*
97+
* Given a `TypedArray` or an `ArrayBuffer` instance `input`, creates a string of the same length
98+
* as input byteLength, using bytes from input as codepoints.
99+
*
100+
* E.g. for `Uint8Array` input, this is similar to `String.fromCodePoint(...input)`.
101+
*
102+
* Wider `TypedArray` inputs, e.g. `Uint16Array`, are interpreted as underlying _bytes_.
103+
*
104+
* @param input - The bytes to decode
105+
* @returns The decoded string
106+
*/
107+
export function isomorphicDecode(input: ArrayBufferLike | ArrayBufferView): string;
108+
109+
/**
110+
* Implements [isomorphic encode](https://infra.spec.whatwg.org/#isomorphic-encode).
111+
*
112+
* Given a string, creates an `Uint8Array` of the same length with the string codepoints as byte values.
113+
*
114+
* Accepts only [isomorphic string](https://infra.spec.whatwg.org/#isomorphic-string) input
115+
* and asserts that, throwing on any strings containing codepoints higher than `U+00FF`.
116+
*
117+
* @param input - The bytes to decode
118+
* @returns An Uint8Array containing the input bytes.
119+
*/
120+
export function isomorphicEncode(str: string): Uint8Array;
121+
93122
/**
94123
* Implements [get an encoding from a string `label`](https://encoding.spec.whatwg.org/#concept-encoding-get).
95124
*

encoding.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,6 @@ export {
1313
getBOMEncoding,
1414
labelToName,
1515
legacyHookDecode,
16+
isomorphicDecode,
17+
isomorphicEncode,
1618
} from './fallback/encoding.js'

fallback/encoding.js

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@
33

44
import { utf16toString, utf16toStringLoose } from '@exodus/bytes/utf16.js'
55
import { utf8fromStringLoose, utf8toString, utf8toStringLoose } from '@exodus/bytes/utf8.js'
6-
import { createSinglebyteDecoder } from '@exodus/bytes/single-byte.js'
6+
import {
7+
createSinglebyteDecoder,
8+
latin1toString,
9+
latin1fromString,
10+
} from '@exodus/bytes/single-byte.js'
711
import labels from './encoding.labels.js'
812
import { fromSource, getBOMEncoding } from './encoding.api.js'
913
import { unfinishedBytes, mergePrefix } from './encoding.util.js'
@@ -357,3 +361,13 @@ export function legacyHookDecode(input, fallbackEncoding = 'utf-8') {
357361

358362
return createSinglebyteDecoder(enc, true)(u8)
359363
}
364+
365+
export function isomorphicDecode(input) {
366+
return latin1toString(fromSource(input))
367+
}
368+
369+
export function isomorphicEncode(str) {
370+
const res = latin1fromString(str)
371+
// match new Uint8Array, which is non-pooled
372+
return res.byteOffset === 0 && res.length === res.buffer.byteLength ? res : res.slice(0)
373+
}

single-byte.d.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,9 @@ export function createSinglebyteEncoder(
108108
* > This is different from `new TextDecoder('iso-8859-1')` and `new TextDecoder('latin1')`, as those
109109
* > alias to `new TextDecoder('windows-1252')`.
110110
*
111+
* Prefer using `isomorphicDecode()` from `@exodus/bytes/encoding.js` or `@exodus/bytes/encoding-lite.js`,
112+
* which is identical to this but allows more input types.
113+
*
111114
* @param arr - The bytes to decode
112115
* @returns The decoded string
113116
*/
@@ -123,6 +126,8 @@ export function latin1toString(arr: Uint8Array): string;
123126
* const latin1fromString = createSinglebyteEncoder('iso-8859-1', { mode: 'fatal' })
124127
* ```
125128
*
129+
* Prefer using `isomorphicEncode()` from `@exodus/bytes/encoding.js` or `@exodus/bytes/encoding-lite.js`.
130+
*
126131
* @param string - The string to encode
127132
* @returns The encoded bytes
128133
*/

0 commit comments

Comments
 (0)