Skip to content

Commit

Permalink
refactor: optimize base64 with tc39/proposal-arraybuffer-base64
Browse files Browse the repository at this point in the history
fixes panva#752
  • Loading branch information
panva committed Feb 22, 2025
1 parent b009a99 commit 8a0da69
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 18 deletions.
47 changes: 35 additions & 12 deletions src/lib/base64url.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,43 @@
import { encoder, decoder, Buffer } from '../lib/buffer_utils.js'
import { encoder, decoder } from '../lib/buffer_utils.js'

export function encodeBase64(input: Uint8Array | string): string {
if (Buffer) return Buffer.from(input).toString('base64')
let unencoded = input
if (typeof unencoded === 'string') {
unencoded = encoder.encode(unencoded)
export function encodeBase64(input: Uint8Array): string {
// @ts-ignore
if (Uint8Array.prototype.toBase64) {
// @ts-ignore
return input.toBase64()
}

const CHUNK_SIZE = 0x8000
const arr = []
for (let i = 0; i < unencoded.length; i += CHUNK_SIZE) {
for (let i = 0; i < input.length; i += CHUNK_SIZE) {
// @ts-expect-error
arr.push(String.fromCharCode.apply(null, unencoded.subarray(i, i + CHUNK_SIZE)))
arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)))
}
return btoa(arr.join(''))
}

export function encode(input: Uint8Array | string): string {
if (Buffer) return Buffer.from(input).toString('base64url')
return encodeBase64(input).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
let unencoded = input
if (typeof unencoded === 'string') {
unencoded = encoder.encode(unencoded)
}

// @ts-ignore
if (Uint8Array.prototype.toBase64) {
// @ts-ignore
return unencoded.toBase64({ alphabet: 'base64url', omitPadding: true })
}

return encodeBase64(unencoded).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_')
}

export function decodeBase64(encoded: string): Uint8Array {
if (Buffer) return Buffer.from(encoded, 'base64')
// @ts-ignore
if (Uint8Array.fromBase64) {
// @ts-ignore
return Uint8Array.fromBase64(encoded)
}

const binary = atob(encoded)
const bytes = new Uint8Array(binary.length)
for (let i = 0; i < binary.length; i++) {
Expand All @@ -31,11 +47,18 @@ export function decodeBase64(encoded: string): Uint8Array {
}

export function decode(input: Uint8Array | string): Uint8Array {
// @ts-ignore
if (Uint8Array.fromBase64) {
// @ts-ignore
return Uint8Array.fromBase64(typeof input === 'string' ? input : decoder.decode(input), {
alphabet: 'base64url',
})
}

let encoded = input
if (encoded instanceof Uint8Array) {
encoded = decoder.decode(encoded)
}
if (Buffer) return Buffer.from(encoded, 'base64url')
encoded = encoded.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, '')
try {
return decodeBase64(encoded)
Expand Down
6 changes: 0 additions & 6 deletions src/lib/buffer_utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,7 @@ export const decoder = new TextDecoder()

const MAX_INT32 = 2 ** 32

// @ts-expect-error
const Buffer: any = globalThis.process?.getBuiltinModule?.('node:buffer')?.Buffer

export { Buffer }

export function concat(...buffers: Uint8Array[]): Uint8Array {
if (Buffer) return Buffer.concat(buffers)
const size = buffers.reduce((acc, { length }) => acc + length, 0)
const buf = new Uint8Array(size)
let i = 0
Expand Down

0 comments on commit 8a0da69

Please sign in to comment.