Skip to content

Commit 0dce311

Browse files
committed
perf: use TextDecoder for building strings
1 parent 334cf40 commit 0dce311

File tree

1 file changed

+28
-12
lines changed

1 file changed

+28
-12
lines changed

index.ts

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -62,40 +62,56 @@ function chain<T extends Chain & AsChain<T>>(...args: T): Coder<Input<First<T>>,
6262
return { encode, decode };
6363
}
6464

65+
// Their length (number of non-default args) is 0 per spec, which native engines follow
66+
// Slow text-encoding polyfill that we want to avoid has length = 2
67+
const nativeDecoder = globalThis.TextDecoder && globalThis.TextDecoder.length === 0 ? new TextDecoder() : null;
68+
6569
/**
6670
* Encodes integer radix representation in Uint8Array to a string in alphabet and back, with optional padding
6771
* @__NO_SIDE_EFFECTS__
6872
*/
6973
function alphabet(letters: string, paddingBits: number = 0, paddingChr = '='): Coder<Uint8Array, string> {
7074
// mapping 1 to "b"
7175
astr('alphabet', letters);
72-
const lettersA = letters.split('');
73-
const len = lettersA.length;
76+
const letterChars = letters.split('');
77+
const len = letterChars.length;
7478
const paddingCode = paddingChr.codePointAt(0)!;
7579
if (paddingChr.length !== 1 || paddingCode > 128) throw new Error('Wrong padding char');
7680

7781
// mapping "b" to 1
7882
const indexes = new Int8Array(256).fill(-1);
79-
lettersA.forEach((l, i) => {
83+
const letterCodes = new Int8Array(256).fill(-1);
84+
letterChars.forEach((l, i) => {
8085
const code = l.codePointAt(0)!;
8186
if (code > 127 || indexes[code] !== -1) throw new Error(`Non-ascii or duplicate symbol: "${l}"`);
8287
indexes[code] = i;
88+
letterCodes[i] = code;
8389
});
8490
return {
8591
encode: (digits: Uint8Array): string => {
8692
abytes(digits);
87-
const out = []
88-
for (const i of digits) {
89-
if (i >= len)
90-
throw new Error(
91-
`alphabet.encode: digit index outside alphabet "${i}". Allowed: ${letters}`
92-
);
93-
out.push(lettersA[i]!);
93+
let out
94+
const length = digits.length
95+
if (nativeDecoder) {
96+
const codes = new Uint8Array(length);
97+
for (let i = 0; i < length; i++) {
98+
const c = letterCodes[digits[i]!]!;
99+
if (c < 0) throw new Error( `alphabet.encode: digit index outside alphabet "${digits[i]}". Allowed: ${letters}`);
100+
codes[i] = c;
101+
}
102+
out = nativeDecoder.decode(codes);
103+
} else {
104+
const acc = []
105+
for (const d of digits) {
106+
if (d >= len) throw new Error(`alphabet.encode: digit index outside alphabet "${d}". Allowed: ${letters}`);
107+
acc.push(letterChars[d]!);
108+
}
109+
out = acc.join('');
94110
}
95111
if (paddingBits > 0) {
96-
while ((out.length * paddingBits) % 8) out.push(paddingChr);
112+
while ((out.length * paddingBits) % 8) out += paddingChr;
97113
}
98-
return out.join('');
114+
return out;
99115
},
100116
decode: (str: string): Uint8Array => {
101117
astr('alphabet.decode', str);

0 commit comments

Comments
 (0)