a collection of lightweight TypeScript packages for AT Protocol, the protocol powering Bluesky
atproto bluesky typescript npm
101
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor(multibase): bun-specific base32 encoding optimization

Mary e4c7516e b9e9152d

+162 -72
+63
packages/utilities/multibase/lib/bases/base32-encode.bun.ts
··· 1 + const ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'; 2 + 3 + // 2-character lookup table: _lut2[(i << 5) | j] = alphabet[i] + alphabet[j] 4 + // JSC (Bun) optimizes string concatenation from pre-built fragments significantly 5 + // better than String.fromCharCode calls 6 + const _lut2: string[] = /*#__PURE__*/ (() => { 7 + const t: string[] = Array.from({ length: 1024 }); 8 + for (let i = 0; i < 32; i++) { 9 + for (let j = 0; j < 32; j++) { 10 + t[(i << 5) | j] = ALPHABET[i] + ALPHABET[j]; 11 + } 12 + } 13 + return t; 14 + })(); 15 + 16 + /** 17 + * encodes a Uint8Array to an unpadded RFC 4648 base32 (lowercase) string 18 + * @param bytes source buffer 19 + * @returns base32 encoded string 20 + */ 21 + export const toBase32 = (bytes: Uint8Array): string => { 22 + const len = bytes.length; 23 + let str = ''; 24 + 25 + // process 5-byte groups (= 40 bits = 8 base32 characters each), 26 + // using the 2-char lookup table to emit pairs of characters at a time 27 + let i = 0; 28 + const fullGroups = len - (len % 5); 29 + for (; i < fullGroups; i += 5) { 30 + const b0 = bytes[i]; 31 + const b1 = bytes[i + 1]; 32 + const b2 = bytes[i + 2]; 33 + const b3 = bytes[i + 3]; 34 + const b4 = bytes[i + 4]; 35 + 36 + str += 37 + _lut2[((b0 >>> 3) << 5) | (((b0 << 2) | (b1 >>> 6)) & 0x1f)] + 38 + _lut2[(((b1 >>> 1) & 0x1f) << 5) | (((b1 << 4) | (b2 >>> 4)) & 0x1f)] + 39 + _lut2[((((b2 << 1) | (b3 >>> 7)) & 0x1f) << 5) | ((b3 >>> 2) & 0x1f)] + 40 + _lut2[((((b3 << 3) | (b4 >>> 5)) & 0x1f) << 5) | (b4 & 0x1f)]; 41 + } 42 + 43 + // handle remaining 1-4 bytes 44 + if (i < len) { 45 + let buffer = 0; 46 + let bits = 0; 47 + for (; i < len; i++) { 48 + buffer = (buffer << 8) | bytes[i]; 49 + bits += 8; 50 + } 51 + while (bits > 0) { 52 + if (bits >= 5) { 53 + bits -= 5; 54 + str += ALPHABET[(buffer >>> bits) & 0x1f]; 55 + } else { 56 + str += ALPHABET[(buffer << (5 - bits)) & 0x1f]; 57 + bits = 0; 58 + } 59 + } 60 + } 61 + 62 + return str; 63 + };
+91
packages/utilities/multibase/lib/bases/base32-encode.ts
··· 1 + const ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'; 2 + 3 + // charCode lookup table: cc[i] = ALPHABET.charCodeAt(i) for i in 0..31 4 + const _cc: Uint8Array = /*#__PURE__*/ (() => { 5 + const t = new Uint8Array(32); 6 + for (let i = 0; i < 32; i++) { 7 + t[i] = ALPHABET.charCodeAt(i); 8 + } 9 + return t; 10 + })(); 11 + 12 + const _fromCharCode = String.fromCharCode; 13 + 14 + /** 15 + * encodes a Uint8Array to an unpadded RFC 4648 base32 (lowercase) string 16 + * @param bytes source buffer 17 + * @returns base32 encoded string 18 + */ 19 + export const toBase32 = (bytes: Uint8Array): string => { 20 + const len = bytes.length; 21 + const full = (len / 5) | 0; 22 + const rem = len - full * 5; 23 + const cc = _cc; 24 + 25 + let str = ''; 26 + let ip = 0; 27 + 28 + // process pairs of 5-byte groups (10 bytes → 16 base32 chars) at a time, 29 + // batching into a single String.fromCharCode call for fewer string concats 30 + const pairs = (full / 2) | 0; 31 + for (let g = 0; g < pairs; g++) { 32 + const a0 = bytes[ip], a1 = bytes[ip + 1], a2 = bytes[ip + 2], a3 = bytes[ip + 3], a4 = bytes[ip + 4]; 33 + const b0 = bytes[ip + 5], b1 = bytes[ip + 6], b2 = bytes[ip + 7], b3 = bytes[ip + 8], b4 = bytes[ip + 9]; 34 + 35 + str += _fromCharCode( 36 + cc[a0 >>> 3], 37 + cc[((a0 << 2) | (a1 >>> 6)) & 0x1f], 38 + cc[(a1 >>> 1) & 0x1f], 39 + cc[((a1 << 4) | (a2 >>> 4)) & 0x1f], 40 + cc[((a2 << 1) | (a3 >>> 7)) & 0x1f], 41 + cc[(a3 >>> 2) & 0x1f], 42 + cc[((a3 << 3) | (a4 >>> 5)) & 0x1f], 43 + cc[a4 & 0x1f], 44 + cc[b0 >>> 3], 45 + cc[((b0 << 2) | (b1 >>> 6)) & 0x1f], 46 + cc[(b1 >>> 1) & 0x1f], 47 + cc[((b1 << 4) | (b2 >>> 4)) & 0x1f], 48 + cc[((b2 << 1) | (b3 >>> 7)) & 0x1f], 49 + cc[(b3 >>> 2) & 0x1f], 50 + cc[((b3 << 3) | (b4 >>> 5)) & 0x1f], 51 + cc[b4 & 0x1f], 52 + ); 53 + ip += 10; 54 + } 55 + 56 + // remaining full group if odd count 57 + if (full & 1) { 58 + const b0 = bytes[ip], b1 = bytes[ip + 1], b2 = bytes[ip + 2], b3 = bytes[ip + 3], b4 = bytes[ip + 4]; 59 + 60 + str += _fromCharCode( 61 + cc[b0 >>> 3], 62 + cc[((b0 << 2) | (b1 >>> 6)) & 0x1f], 63 + cc[(b1 >>> 1) & 0x1f], 64 + cc[((b1 << 4) | (b2 >>> 4)) & 0x1f], 65 + cc[((b2 << 1) | (b3 >>> 7)) & 0x1f], 66 + cc[(b3 >>> 2) & 0x1f], 67 + cc[((b3 << 3) | (b4 >>> 5)) & 0x1f], 68 + cc[b4 & 0x1f], 69 + ); 70 + ip += 5; 71 + } 72 + 73 + // handle remaining 1-4 bytes 74 + if (rem > 0) { 75 + let buffer = 0; 76 + let bits = 0; 77 + for (let i = ip; i < len; i++) { 78 + buffer = (buffer << 8) | bytes[i]; 79 + bits += 8; 80 + } 81 + while (bits >= 5) { 82 + bits -= 5; 83 + str += _fromCharCode(cc[(buffer >>> bits) & 0x1f]); 84 + } 85 + if (bits > 0) { 86 + str += _fromCharCode(cc[(buffer << (5 - bits)) & 0x1f]); 87 + } 88 + } 89 + 90 + return str; 91 + };
+3 -72
packages/utilities/multibase/lib/bases/base32.ts
··· 1 - import { allocUnsafe, decodeUtf8From } from '@atcute/uint8array'; 2 - 3 - const ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'; 4 - 5 - // #region encode 1 + import { allocUnsafe } from '@atcute/uint8array'; 6 2 7 - // charCode lookup table: _encLut[i] = ALPHABET.charCodeAt(i) for i in 0..31 8 - const _encLut: Uint8Array = /*#__PURE__*/ (() => { 9 - const t = new Uint8Array(32); 10 - for (let i = 0; i < 32; i++) { 11 - t[i] = ALPHABET.charCodeAt(i); 12 - } 13 - return t; 14 - })(); 15 - 16 - // output length for a given remainder (0-4 trailing bytes after full 5-byte groups) 17 - const _remOutLen = [0, 2, 4, 5, 7]; 18 - 19 - /** 20 - * encodes a Uint8Array to an unpadded RFC 4648 base32 (lowercase) string 21 - * @param bytes source buffer 22 - * @returns base32 encoded string 23 - */ 24 - export const toBase32 = (bytes: Uint8Array): string => { 25 - const len = bytes.length; 26 - const full = (len / 5) | 0; 27 - const rem = len - full * 5; 28 - const outLen = full * 8 + _remOutLen[rem]; 29 - const out = allocUnsafe(outLen); 30 - const cc = _encLut; 3 + export { toBase32 } from '#bases/base32-encode'; 31 4 32 - // process 5-byte groups (= 40 bits = 8 base32 characters each) 33 - let ip = 0; 34 - let op = 0; 35 - for (let g = 0; g < full; g++) { 36 - const b0 = bytes[ip++]; 37 - const b1 = bytes[ip++]; 38 - const b2 = bytes[ip++]; 39 - const b3 = bytes[ip++]; 40 - const b4 = bytes[ip++]; 41 - 42 - out[op++] = cc[b0 >>> 3]; 43 - out[op++] = cc[((b0 << 2) | (b1 >>> 6)) & 0x1f]; 44 - out[op++] = cc[(b1 >>> 1) & 0x1f]; 45 - out[op++] = cc[((b1 << 4) | (b2 >>> 4)) & 0x1f]; 46 - out[op++] = cc[((b2 << 1) | (b3 >>> 7)) & 0x1f]; 47 - out[op++] = cc[(b3 >>> 2) & 0x1f]; 48 - out[op++] = cc[((b3 << 3) | (b4 >>> 5)) & 0x1f]; 49 - out[op++] = cc[b4 & 0x1f]; 50 - } 51 - 52 - // handle remaining 1-4 bytes 53 - if (rem > 0) { 54 - let buffer = 0; 55 - let bits = 0; 56 - for (let i = ip; i < len; i++) { 57 - buffer = (buffer << 8) | bytes[i]; 58 - bits += 8; 59 - } 60 - while (bits > 0) { 61 - if (bits >= 5) { 62 - bits -= 5; 63 - out[op++] = cc[(buffer >>> bits) & 0x1f]; 64 - } else { 65 - out[op++] = cc[(buffer << (5 - bits)) & 0x1f]; 66 - bits = 0; 67 - } 68 - } 69 - } 70 - 71 - return decodeUtf8From(out); 72 - }; 73 - 74 - // #endregion 5 + const ALPHABET = 'abcdefghijklmnopqrstuvwxyz234567'; 75 6 76 7 // #region decode 77 8
+4
packages/utilities/multibase/package.json
··· 22 22 "node": "./dist/bases/base16-node.js", 23 23 "default": "./dist/bases/base16-web.js" 24 24 }, 25 + "#bases/base32-encode": { 26 + "bun": "./dist/bases/base32-encode.bun.js", 27 + "default": "./dist/bases/base32-encode.js" 28 + }, 25 29 "#bases/base64": { 26 30 "bun": "./dist/bases/base64-web.js", 27 31 "workerd": "./dist/bases/base64-web.js",
+1
packages/utilities/multibase/tsconfig.json
··· 25 25 "stripInternal": true, 26 26 "paths": { 27 27 "#bases/base16": ["./lib/bases/base16-web.ts"], 28 + "#bases/base32-encode": ["./lib/bases/base32-encode.ts"], 28 29 "#bases/base64": ["./lib/bases/base64-web.ts"] 29 30 } 30 31 },