Skip to content

Commit

Permalink
Merge pull request #180 from Scille/improve-b64-encoding
Browse files Browse the repository at this point in the history
Revert usage to `btoa` and `atob` for base64 encoding/decoding
  • Loading branch information
Max-7 authored Feb 3, 2025
2 parents a402b25 + 8e85ef6 commit a003d0f
Show file tree
Hide file tree
Showing 2 changed files with 34 additions and 105 deletions.
12 changes: 12 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# EditorConfig is awesome: https://EditorConfig.org

# top-most EditorConfig file
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
127 changes: 22 additions & 105 deletions lib/common/base64.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
// Parsec Cloud (https://parsec.cloud) Copyright (c) BUSL-1.1 2016-present Scille SAS

const KEY_STR = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';

function fromObject(obj: object): string {
const jsonString = JSON.stringify(obj);
return encode(jsonString);
Expand All @@ -12,67 +10,31 @@ function toObject(data: string): object {
return JSON.parse(jsonString);
}

/*
* Base64 encode / decode
* Taken from http://www.webtoolkit.info/
* and updated a bit
*/

function encode(input: string): string {
let output = '';
let i = 0;

input = _utf8Encode(input);

while (i < input.length) {
const chr1 = input.charCodeAt(i++);
const chr2 = input.charCodeAt(i++);
const chr3 = input.charCodeAt(i++);

const enc1 = chr1 >> 2;
const enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
let enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
let enc4 = chr3 & 63;

if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}

output = output + KEY_STR.charAt(enc1) + KEY_STR.charAt(enc2) + KEY_STR.charAt(enc3) + KEY_STR.charAt(enc4);
}

return output;
const encoder = new TextEncoder();
// Encode the string to utf-8, so it's safe to use with `btoa`.
// CF: https://developer.mozilla.org/en-US/docs/Web/API/Window/btoa#unicode_strings
const bytes: Uint8Array = encoder.encode(input);
// Convert the bytes array to a string since `btoa` only accepts strings.
const binString = Array.from(bytes, (c) => String.fromCharCode(c)).join('');
// Replace special char to be url safe.
return btoa(binString).replace(/\+/g, '-').replace(/\//g, '_');
}

function decode(input: string): string {
let output = '';
let i = 0;

input = input.replace(/[^A-Za-z0-9+/=]/g, '');

while (i < input.length) {
const enc1 = KEY_STR.indexOf(input.charAt(i++));
const enc2 = KEY_STR.indexOf(input.charAt(i++));
const enc3 = KEY_STR.indexOf(input.charAt(i++));
const enc4 = KEY_STR.indexOf(input.charAt(i++));

const chr1 = (enc1 << 2) | (enc2 >> 4);
const chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
const chr3 = ((enc3 & 3) << 6) | enc4;

output = output + String.fromCharCode(chr1);

if (enc3 !== 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 !== 64) {
output = output + String.fromCharCode(chr3);
}
}
output = _utf8Decode(output);
return output;
function decode(b64url: string): string {
// Replace back the special chars used to make base64 string url safe.
const input = b64url.replace(/-/g, '+').replace(/_/g, '\\');
const binString = atob(input);
const bytes = Uint8Array.from(
binString,
(c) =>
// codePointAt may return undefined if the char is outside of valid UTF-16 range,
// But since we come from a base64 encoded string, we know that all chars are valid
c.codePointAt(0)!,
);
const decoder = new TextDecoder();
// Decode the utf-8 bytes to a string
return decoder.decode(bytes);
}

export const Base64 = {
Expand All @@ -81,48 +43,3 @@ export const Base64 = {
encode,
decode,
};

function _utf8Encode(s: string): string {
s = s.replace(/\r\n/g, '\n');
let utfText = '';

for (let n = 0; n < s.length; n++) {
const c = s.charCodeAt(n);
if (c < 128) {
utfText += String.fromCharCode(c);
} else if (c > 127 && c < 2048) {
utfText += String.fromCharCode((c >> 6) | 192);
utfText += String.fromCharCode((c & 63) | 128);
} else {
utfText += String.fromCharCode((c >> 12) | 224);
utfText += String.fromCharCode(((c >> 6) & 63) | 128);
utfText += String.fromCharCode((c & 63) | 128);
}
}

return utfText;
}

function _utf8Decode(utfText: string): string {
let s = '';
let i = 0;

while (i < utfText.length) {
const c = utfText.charCodeAt(i);

if (c < 128) {
s += String.fromCharCode(c);
i++;
} else if (c > 191 && c < 224) {
const c2 = utfText.charCodeAt(i + 1);
s += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
i += 2;
} else {
const c2 = utfText.charCodeAt(i + 1);
const c3 = utfText.charCodeAt(i + 2);
s += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
i += 3;
}
}
return s;
}

0 comments on commit a003d0f

Please sign in to comment.