Skip to content

Commit 5a8c19b

Browse files
committed
feat: make Base32 browser compatible
— Remove dependency on Node's Buffer and switch to Uint8Array for byte handling and TextDecoder for string decoding, with a globalThis.Buffer fallback for Node environments. Signed-off-by: Chen Su <ghosind@gmail.com>
1 parent 8d0a179 commit 5a8c19b

4 files changed

Lines changed: 72 additions & 16 deletions

File tree

package-lock.json

Lines changed: 2 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/base32.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/* eslint-disable no-bitwise */
2-
import { checkEncoder, checkPadChar } from './utils';
2+
import { checkEncoder, checkPadChar, decodeBytes } from './utils';
33

44
/**
55
* The standard Base32 encoding alphabets.
@@ -82,7 +82,7 @@ export class Base32Encoding {
8282

8383
const { encoder, padChar } = this.getEncoderAndPadChar(options);
8484
const destLen = this.getEncodeLength(data.length, padChar);
85-
const dest = Buffer.alloc(destLen, padChar);
85+
const dest = new Uint8Array(destLen);
8686

8787
let bits = 0;
8888
let value = 0;
@@ -93,25 +93,25 @@ export class Base32Encoding {
9393
bits += 8;
9494

9595
while (bits >= 5) {
96-
dest.writeUInt8(encoder[(value >>> (bits - 5)) & 31].charCodeAt(0), offset);
96+
dest[offset] = encoder[(value >>> (bits - 5)) & 31].charCodeAt(0);
9797
offset++;
9898
bits -= 5;
9999
}
100100
}
101101

102102
if (bits > 0) {
103-
dest.writeUInt8(encoder[(value << (5 - bits)) & 31].charCodeAt(0), offset);
103+
dest[offset] = encoder[(value << (5 - bits)) & 31].charCodeAt(0);
104104
offset++;
105105
}
106106

107107
if (padChar !== '') {
108108
while (offset < destLen) {
109-
dest.writeUInt8(padChar.charCodeAt(0), offset);
109+
dest[offset] = padChar.charCodeAt(0);
110110
offset++;
111111
}
112112
}
113113

114-
return dest.toString();
114+
return decodeBytes(dest);
115115
}
116116

117117
/**
@@ -133,7 +133,7 @@ export class Base32Encoding {
133133

134134
const { encoder, padChar } = this.getEncoderAndPadChar(options);
135135
const [cleanedLength, destLen] = this.getDecodeLength(str, padChar);
136-
const dest = Buffer.alloc(destLen);
136+
const dest = new Uint8Array(destLen);
137137

138138
let bits = 0;
139139
let value = 0;
@@ -149,13 +149,13 @@ export class Base32Encoding {
149149
bits += 5;
150150

151151
if (bits >= 8) {
152-
dest.writeUInt8((value >>> (bits - 8)) & 0xff, offset);
152+
dest[offset] = (value >>> (bits - 8)) & 0xff;
153153
offset++;
154154
bits -= 8;
155155
}
156156
}
157157

158-
return dest.toString();
158+
return decodeBytes(dest);
159159
}
160160

161161
/**

src/utils.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,3 +56,21 @@ export const checkPadChar = (padChar: string): string | undefined => {
5656

5757
return padding;
5858
};
59+
60+
/**
61+
* Decode the Uint8Array bytes to string.
62+
*
63+
* @param bytes The bytes to decode.
64+
* @returns The decoded string.
65+
*/
66+
export const decodeBytes = (bytes: Uint8Array): string => {
67+
if (typeof TextDecoder !== 'undefined') {
68+
return new TextDecoder().decode(bytes);
69+
}
70+
71+
if (typeof globalThis.Buffer !== 'undefined') {
72+
return globalThis.Buffer.from(bytes).toString();
73+
}
74+
75+
return Array.from(bytes).map((b) => String.fromCharCode(b)).join('');
76+
}

test/utils.spec.ts

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import assert from 'assert';
22
import { describe, it } from 'mocha';
33

44
import { Base32StdEncoder } from '../src/base32';
5-
import { checkEncoder, checkPadChar } from '../src/utils';
5+
import { checkEncoder, checkPadChar, decodeBytes } from '../src/utils';
66

77
describe('Test check encoder', () => {
88
it('Test check encoder', () => {
@@ -64,3 +64,45 @@ describe('Test check pad char', () => {
6464
});
6565
});
6666
});
67+
68+
describe('Test decode bytes', () => {
69+
const data = new Uint8Array([72, 101, 108, 108, 111, 44, 32, 87, 111, 114, 108, 100, 33]); // "Hello, World!"
70+
const expected = "Hello, World!";
71+
72+
it('Test decode bytes with default TextDecoder', () => {
73+
const str = decodeBytes(data);
74+
assert.equal(str, expected);
75+
});
76+
77+
it('Test decode bytes with Node.js Buffer', () => {
78+
const original = global.TextDecoder;
79+
80+
try {
81+
// @ts-ignore
82+
global.TextDecoder = undefined;
83+
84+
const str = decodeBytes(data);
85+
assert.equal(str, expected);
86+
} finally {
87+
global.TextDecoder = original;
88+
}
89+
});
90+
91+
it('Test decode bytes with convert fromCharCode', () => {
92+
const originalTextDecoder = global.TextDecoder;
93+
const originalBuffer = global.Buffer;
94+
95+
try {
96+
// @ts-ignore
97+
global.TextDecoder = undefined;
98+
// @ts-ignore
99+
global.Buffer = undefined;
100+
101+
const str = decodeBytes(data);
102+
assert.equal(str, expected);
103+
} finally {
104+
global.TextDecoder = originalTextDecoder;
105+
global.Buffer = originalBuffer;
106+
}
107+
});
108+
});

0 commit comments

Comments
 (0)