Skip to content

Instantly share code, notes, and snippets.

@peterprokop
Created October 7, 2021 14:28
Show Gist options
  • Save peterprokop/3345b5a683362a55e3b3a7254cc58e75 to your computer and use it in GitHub Desktop.
Save peterprokop/3345b5a683362a55e3b3a7254cc58e75 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html>
<script type="text/javascript">
//<![CDATA[
function randomArray(length, max) {
return Array.apply(null, Array(length)).map(function() {
return Math.round(Math.random() * max);
});
}
function regularBase64FromURLSafe(safe) {
regular = safe.replace(/-/g, "+")
.replace(/_/g, "/");
for (i = 0; i < regular.length % 4; i++) {
regular += "=";
}
return regular;
}
function urlSafeBase64FromRegular(regular) {
return regular.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}
/// Converts UInt8 array to URL-safe Base64-encoded string
function _uint8ToBase64 (uint8arr) {
const regularB64 = btoa(String.fromCharCode.apply(null, uint8arr));
return urlSafeBase64FromRegular(regularB64);
}
/// Converts URL-safe Base64-encoded string to UInt8 array
function _b64ToByteArray (b64) {
const regularB64 = regularBase64FromURLSafe(b64);
return Uint8Array.from(atob(regularB64), c => c.charCodeAt(0));
}
var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
var PLUS = '+'.charCodeAt(0)
var SLASH = '/'.charCodeAt(0)
var NUMBER = '0'.charCodeAt(0)
var LOWER = 'a'.charCodeAt(0)
var UPPER = 'A'.charCodeAt(0)
var PLUS_URL_SAFE = '-'.charCodeAt(0)
var SLASH_URL_SAFE = '_'.charCodeAt(0)
function decode (elt) {
var code = elt.charCodeAt(0)
if (code === PLUS || code === PLUS_URL_SAFE) return 62 // '+'
if (code === SLASH || code === SLASH_URL_SAFE) return 63 // '/'
if (code < NUMBER) return -1 // no match
if (code < NUMBER + 10) return code - NUMBER + 26 + 26
if (code < UPPER + 26) return code - UPPER
if (code < LOWER + 26) return code - LOWER + 26
}
function b64ToByteArray (b64) {
var i, j, l, tmp, placeHolders, arr
// if (b64.length % 4 > 0) {
// throw new Error('Invalid string. Length must be a multiple of 4')
// }
// the number of equal signs (place holders)
// if there are two placeholders, than the two characters before it
// represent one byte
// if there is only one, then the three characters before it represent 2 bytes
// this is just a cheap hack to not do indexOf twice
var len = b64.length
placeHolders = b64.charAt(len - 2) === '=' ? 2 : b64.charAt(len - 1) === '=' ? 1 : 0
// base64 is 4/3 + up to two characters of the original data
arr = new Uint8Array(b64.length * 3 / 4 - placeHolders)
// if there are placeholders, only get up to the last complete 4 chars
l = placeHolders > 0 ? b64.length - 4 : b64.length
var L = 0
function push (v) {
arr[L++] = v
}
for (i = 0, j = 0; i < l; i += 4, j += 3) {
tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
push((tmp & 0xFF0000) >> 16)
push((tmp & 0xFF00) >> 8)
push(tmp & 0xFF)
}
if (placeHolders === 2) {
tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
push(tmp & 0xFF)
} else if (placeHolders === 1) {
tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
push((tmp >> 8) & 0xFF)
push(tmp & 0xFF)
}
return arr
}
function uint8ToBase64 (uint8) {
var i
var extraBytes = uint8.length % 3 // if we have 1 byte left, pad 2 bytes
var output = ''
var temp, length
function encode (num) {
return lookup.charAt(num)
}
function tripletToBase64 (num) {
return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
}
// go through the array every three bytes, we'll deal with trailing stuff later
for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
output += tripletToBase64(temp)
}
// pad the end with zeros, but make sure to not forget the extra bytes
switch (extraBytes) {
case 1:
temp = uint8[uint8.length - 1]
output += encode(temp >> 2)
output += encode((temp << 4) & 0x3F)
output += '=='
break
case 2:
temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
output += encode(temp >> 10)
output += encode((temp >> 4) & 0x3F)
output += encode((temp << 2) & 0x3F)
output += '='
break
default:
break
}
return output.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
}
function arrayEquals(a, b) {
for (var i = 0; i < a.length; i++) {
if (a[i] != b[i]) {
console.log("Not equal", a[i], b[i]);
return false;
}
}
return true;
}
function test() {
for (var j = 0; j <= 10000; j++) {
// console.log("Working", j);
const arrayLength = Math.round(Math.random() * 1000);
var arr = randomArray(arrayLength, 255);
var first = _uint8ToBase64(arr);
var second = uint8ToBase64(arr);
if (first != second) {
console.log("Not equal!")
console.log(first);
console.log(second);
}
var decodedFirst = _b64ToByteArray(first);
var decodedSecond = b64ToByteArray(second);
// console.log(decodedFirst);
// console.log(decodedSecond);
if(!arrayEquals(decodedFirst, decodedSecond)) {
console.log("Not equal!")
console.log(decodedFirst);
console.log(decodedSecond);
}
}
}
try {
test();
} catch (error) {
console.error(error);
}
console.log("Done");
//]]>
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment