Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
https://www.npmjs.com/package/byte-base64 - Encode JS Uint8Array, simple array of bytes or native JS string to base64 and back
/*
MIT License
Copyright (c) 2020 Egor Nepomnyaschih
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
*/
/*
// This constant can also be computed with the following algorithm:
const base64abc = [],
A = "A".charCodeAt(0),
a = "a".charCodeAt(0),
n = "0".charCodeAt(0);
for (let i = 0; i < 26; ++i) {
base64abc.push(String.fromCharCode(A + i));
}
for (let i = 0; i < 26; ++i) {
base64abc.push(String.fromCharCode(a + i));
}
for (let i = 0; i < 10; ++i) {
base64abc.push(String.fromCharCode(n + i));
}
base64abc.push("+");
base64abc.push("/");
*/
const base64abc = [
"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
"N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z",
"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
"n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "+", "/"
];
/*
// This constant can also be computed with the following algorithm:
const l = 256, base64codes = new Uint8Array(l);
for (let i = 0; i < l; ++i) {
base64codes[i] = 255; // invalid character
}
base64abc.forEach((char, index) => {
base64codes[char.charCodeAt(0)] = index;
});
base64codes["=".charCodeAt(0)] = 0; // ignored anyway, so we just need to prevent an error
*/
const base64codes = [
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63,
52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 0, 255, 255,
255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255,
255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51
];
function getBase64Code(charCode) {
if (charCode >= base64codes.length) {
throw new Error("Unable to parse base64 string.");
}
const code = base64codes[charCode];
if (code === 255) {
throw new Error("Unable to parse base64 string.");
}
return code;
}
export function bytesToBase64(bytes) {
let result = '', i, l = bytes.length;
for (i = 2; i < l; i += 3) {
result += base64abc[bytes[i - 2] >> 2];
result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
result += base64abc[((bytes[i - 1] & 0x0F) << 2) | (bytes[i] >> 6)];
result += base64abc[bytes[i] & 0x3F];
}
if (i === l + 1) { // 1 octet yet to write
result += base64abc[bytes[i - 2] >> 2];
result += base64abc[(bytes[i - 2] & 0x03) << 4];
result += "==";
}
if (i === l) { // 2 octets yet to write
result += base64abc[bytes[i - 2] >> 2];
result += base64abc[((bytes[i - 2] & 0x03) << 4) | (bytes[i - 1] >> 4)];
result += base64abc[(bytes[i - 1] & 0x0F) << 2];
result += "=";
}
return result;
}
export function base64ToBytes(str) {
if (str.length % 4 !== 0) {
throw new Error("Unable to parse base64 string.");
}
const index = str.indexOf("=");
if (index !== -1 && index < str.length - 2) {
throw new Error("Unable to parse base64 string.");
}
let missingOctets = str.endsWith("==") ? 2 : str.endsWith("=") ? 1 : 0,
n = str.length,
result = new Uint8Array(3 * (n / 4)),
buffer;
for (let i = 0, j = 0; i < n; i += 4, j += 3) {
buffer =
getBase64Code(str.charCodeAt(i)) << 18 |
getBase64Code(str.charCodeAt(i + 1)) << 12 |
getBase64Code(str.charCodeAt(i + 2)) << 6 |
getBase64Code(str.charCodeAt(i + 3));
result[j] = buffer >> 16;
result[j + 1] = (buffer >> 8) & 0xFF;
result[j + 2] = buffer & 0xFF;
}
return result.subarray(0, result.length - missingOctets);
}
export function base64encode(str, encoder = new TextEncoder()) {
return bytesToBase64(encoder.encode(str));
}
export function base64decode(str, decoder = new TextDecoder()) {
return decoder.decode(base64ToBytes(str));
}
@enepomnyaschih

This comment has been minimized.

Copy link
Owner Author

@enepomnyaschih enepomnyaschih commented Jul 19, 2019

Test

	// Man = 0x4D, 0x61, 0x6E = TWFu (example from https://en.wikipedia.org/wiki/Base64)
	// Space = 0x20 = 0010 0000
	// Ё = 1101 0000 1000 0001
	// 𤭢 = 1111 0000 1010 0100 1010 1101 1010 0010
	// So, we get sixtets: 001000 001101 000010 000001 111100 001010 010010 101101 101000 10____
	//                     I      N      C      B      8      K      S      t      o      g = =
	expect(base64encode("Man Ё𤭢")).toBe("TWFuINCB8KStog==");

Simple btoa fails here.

@fatso83

This comment has been minimized.

Copy link

@fatso83 fatso83 commented Sep 18, 2019

Push this as a micro-lib - or someone will 😃

@nothingasis

This comment has been minimized.

Copy link

@nothingasis nothingasis commented Dec 4, 2019

This really came in handy, thanks!

@bangonkali

This comment has been minimized.

Copy link

@bangonkali bangonkali commented Dec 10, 2019

Awesome!

@SeaGuy

This comment has been minimized.

Copy link

@SeaGuy SeaGuy commented Feb 5, 2020

You saved me a lot of time, thanks!

@andrey-zakharov

This comment has been minimized.

Copy link

@andrey-zakharov andrey-zakharov commented Feb 25, 2020

base64abc should be compile-time const, dude. all sup

@davidcallanan

This comment has been minimized.

Copy link

@davidcallanan davidcallanan commented Apr 10, 2020

What about a reverse?

@enepomnyaschih

This comment has been minimized.

Copy link
Owner Author

@enepomnyaschih enepomnyaschih commented Apr 11, 2020

@davidcallanan - I've got it in TypeScript https://gist.github.com/enepomnyaschih/54c437997f8202871278d0fdf68148ca. Will add it in JS shortly.

@enepomnyaschih

This comment has been minimized.

Copy link
Owner Author

@enepomnyaschih enepomnyaschih commented Apr 12, 2020

@fatso83 - Created a micro-lib https://www.npmjs.com/package/byte-base64

@andrey-zakharov - Changed base64abc to a constant.

@davidcallanan - Added base64ToBytes and base64decode.

Thank you all for your feedback!

@hondero2552

This comment has been minimized.

Copy link

@hondero2552 hondero2552 commented May 17, 2020

This is simply the BEST solution. Very nice indeed. Thank you brother!

@peaBerberian

This comment has been minimized.

Copy link

@peaBerberian peaBerberian commented Jun 1, 2020

There is a small mistake

	if (charCode > base64codes.length) {

should be:

	if (charCode >= base64codes.length) {

Or else converting something with an "{" inside will not throw the corresponding "Unable to parse base64 string." error.

@enepomnyaschih

This comment has been minimized.

Copy link
Owner Author

@enepomnyaschih enepomnyaschih commented Jun 1, 2020

@peaBerberian - Thanks! Fixed.

@glr0221

This comment has been minimized.

Copy link

@glr0221 glr0221 commented Sep 1, 2020

This is awesome. Thank you very much for sharing.

@retog

This comment has been minimized.

Copy link

@retog retog commented Jan 19, 2021

If the goal is to create a data-url there's an easier approach

            const reader = new FileReader();
            reader.addEventListener("load", function () {
              const dataUrl = reader.result
              ....
            }, false);
            reader.readAsDataURL(new Blob(data))

https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readAsDataURL

@jaure96

This comment has been minimized.

Copy link

@jaure96 jaure96 commented Jul 20, 2021

Awesome!

@rleddy

This comment has been minimized.

Copy link

@rleddy rleddy commented Aug 2, 2021

Happy to find this.

So, I have this repository that I call alpha-copious. It is supposed to keep base code that gets placed in templates for generating a website. So, I was just putting in hashes and went looking for base64. This works.

I put it into a modules. Directory. And, I put in another string for the url version.

I just wrote some of these in C++. You can find both items at copious-world Might wrap those in an encsripten package.

CODINGS is where I am putting C++.

alpha-copious is the nascent packaging program. I suppose I will be stuck putting things into modules. Will have to work on that fairly soon.

I don't have any money for offering bounties. In fact, I am worried about my grocery bill at the moment. So, looking for help in all areas. But, I have a number of crypto.subtle wrappers that I will probably move into alpha-copious. Alpha-copious is named so just because that is where I am putting basic helper code that can be rolled into templates, which are then populated with stuff that appears on a web site. So, its where a lot of things start. And, many of those things might be accessible globally.

So, a hash is very basic. And, I like to call something like do_hash("this") and note .do_hash("oh i forgot what went here").

I imagine a web programmer selecting the modules they want in their template (say with a checkbox html page) and then those modules get generated. "export" and not "exports" because the first is standard. Also, trying to make a number of things accessible from the global context for the web pages - looking for a good balance. Trying to avoid things being too wrapped up. But, it is good to keep the junk in the modules and out of site so to speak.

Using Svelte at the moment. So, running webpack is OK. At least they output modules that can be lazy loaded from a database. While lots of packagers seem to be on the verge of compiling into rigid objects like binaries. They also have huge amounts of code, often. Although I think I saw that in one (webpack ?) that they have a simple small module, which should be about right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment