Skip to content

Instantly share code, notes, and snippets.

@shanewholloway
Forked from jonleighton/base64ArrayBuffer.js
Last active February 13, 2018 05:40
Show Gist options
  • Save shanewholloway/0a0357e2da6dd9147294c3c076cea28d to your computer and use it in GitHub Desktop.
Save shanewholloway/0a0357e2da6dd9147294c3c076cea28d to your computer and use it in GitHub Desktop.
Encode an ArrayBuffer as a base64 string
const base64_encode_std = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
const base64_encode_url = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'
const base64_decode = [{}, {}, {}, {}]
{
const add = (k, i) => {
base64_decode[0][k] = i << 0
base64_decode[1][k] = i << 6
base64_decode[2][k] = i << 12
base64_decode[3][k] = i << 18
}
for (let i=0; i<64; i++)
add(base64_encode_std[i], i)
add('-', 62)
add('_', 63)
add('=', 0)
}
const arrBase64 = bindArrayBufferBase64()
function bindArrayBufferBase64(enc, pad) {
const [dec_d, dec_c, dec_b, dec_a] = base64_decode
if (!enc) {
enc = base64_encode_std
if (null == pad) pad = '='
} else if (true === enc) {
enc = base64_encode_url
pad = ''
}
if (null == pad) pad = ''
return { pack, unpack }
function pack(arrayBuffer) {
const z = new Uint8Array(arrayBuffer.buffer || arrayBuffer)
const rem = z.byteLength % 3, len = z.byteLength - rem
let res = ''
for (let i=0; i < len; i+=3) {
const a = z[i] >>> 2
const b = 0x3f & ( (z[i] << 4) | (z[i+1] >>> 4) )
const c = 0x3f & ( (z[i+1] << 2) | (z[i+2] >>> 6) )
const d = 0x3f & z[i+2]
res += `${enc[a]}${enc[b]}${enc[c]}${enc[d]}`
}
if (2 === rem) {
const a = z[len] >>> 2
const b = 0x3f & ( (z[len] << 4) | (z[len+1] >>> 4) )
const c = 0x0f & z[len+1]
res += `${enc[a]}${enc[b]}${enc[c]}${pad}`
} else if (1 === rem) {
const a = z[len] >> 2
const b = 0x03 & z[len]
res += `${enc[a]}${enc[b]}${pad}${pad}`
}
return res
}
function unpack(str) {
let len=str.length, rem=len&3
if (0 === rem) {
const pad = str.slice(-2)
rem = ('='!==pad[1] ? 0 : '='!==pad[0] ? 2 : 1)
len = 3*(len-rem >> 2) //+ rem
} else {
rem -= 1
len = 3*(len >> 2) //+ rem
}
const z = new Uint8Array(len)
let i=0,j=0
for (; i<len; i+=3,j+=4) {
const v = dec_a[str[j]] | dec_b[str[j+1]] | dec_c[str[j+2]] | dec_d[str[j+3]]
z[ i ] = 0xff & (v >>> 16)
z[i+1] = 0xff & (v >>> 8)
z[i+2] = 0xff & v
}
if (2 === rem) {
const v = dec_b[str[j]] | dec_c[str[j+1]] | dec_d[str[j+2]]
z[ i ] = 0xff & (v >>> 8)
z[i+1] = 0xff & v
} else if (1 === rem) {
const v = dec_c[str[j]] | dec_d[str[j+1]]
z[ i ] = 0xff & v
}
return z
}
}
const {randomBytes} = require('crypto')
function test(count=1, width=64) {
if (0 > width) width = 0 | 1 - width * Math.random()
while (--count > 0) {
const b0 = new Uint8Array(randomBytes(width))
const sz_b64 = arrBase64.pack(b0)
const b1 = arrBase64.unpack(sz_b64)
const b2 = Array.from(b0, (v,i) => ((b1[i] - v) || ''))
console.log(b2.join('.'))
}
}
console.log()
test(10, 63)
console.log()
test(10, 64)
console.log()
test(10, 65)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment