Created
October 7, 2021 14:28
-
-
Save peterprokop/3345b5a683362a55e3b3a7254cc58e75 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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