-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
9 changed files
with
253 additions
and
9 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# uint8-base64 | ||
|
||
[![NPM version][npm-image]][npm-url] | ||
[![build status][ci-image]][ci-url] | ||
[![Test coverage][codecov-image]][codecov-url] | ||
[![npm download][download-image]][download-url] | ||
|
||
You can find a lot of NPM libraries dealing with base64 encoding and decoding. | ||
|
||
However we could not find one that would have as input AND output an Uint8Array. This library does exactly this. | ||
|
||
This library is pretty fast and will convert over 500 Mb per second in nodejs as well as in the browser. | ||
|
||
## Installation | ||
|
||
`$ npm i uint8-base64` | ||
|
||
## Usage | ||
|
||
```js | ||
import { encode } from 'uint8-base64'; | ||
|
||
const result = myModule(args); | ||
// result is ... | ||
``` | ||
|
||
## License | ||
|
||
The code was largely inspired by: https://gist.github.com/enepomnyaschih/72c423f727d395eeaa09697058238727 | ||
|
||
[MIT](./LICENSE) | ||
|
||
[npm-image]: https://img.shields.io/npm/v/uint8-base64.svg | ||
[npm-url]: https://www.npmjs.com/package/uint8-base64 | ||
[ci-image]: https://github.com/cheminfo/uint8-base64/workflows/Node.js%20CI/badge.svg?branch=main | ||
[ci-url]: https://github.com/cheminfo/uint8-base64/actions?query=workflow%3A%22Node.js+CI%22 | ||
[codecov-image]: https://img.shields.io/codecov/c/github/cheminfo/uint8-base64.svg | ||
[codecov-url]: https://codecov.io/gh/cheminfo/uint8-base64 | ||
[download-image]: https://img.shields.io/npm/dm/uint8-base64.svg | ||
[download-url]: https://www.npmjs.com/package/uint8-base64 |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
'use strict'; | ||
|
||
const { Buffer } = require('buffer'); | ||
|
||
const { decode, encode } = require('../lib/'); | ||
|
||
const textEncoder = new TextEncoder(); | ||
const textDecoder = new TextDecoder(); | ||
|
||
let string = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNIOQRSTUVWXYZ'; | ||
for (let i = 0; i < 20; i++) { | ||
string += string; | ||
} | ||
|
||
const uint8 = textEncoder.encode(string); | ||
const buffer = Buffer.from(string, 'utf8'); | ||
|
||
console.time('btoa'); | ||
const btoaString = btoa(string); | ||
console.timeEnd('btoa'); | ||
|
||
console.time('atob'); | ||
const atobString = atob(btoaString); | ||
console.timeEnd('atob'); | ||
|
||
console.time('buffer.toString'); | ||
const bufferToString = buffer.toString('base64'); | ||
console.timeEnd('buffer.toString'); | ||
|
||
console.time('Buffer.from'); | ||
const bufferFrom = Buffer.from(bufferToString, 'base64'); | ||
console.timeEnd('Buffer.from'); | ||
|
||
console.time('encode'); | ||
const bufferBase64 = encode(uint8); | ||
console.timeEnd('encode'); | ||
|
||
console.time('decode'); | ||
const newBuffer = decode(bufferBase64); | ||
console.timeEnd('decode'); | ||
|
||
const newString = textDecoder.decode(newBuffer); | ||
|
||
console.log( | ||
string.length, | ||
uint8.length, | ||
atobString.length, | ||
bufferFrom.length, | ||
bufferBase64.length, | ||
bufferToString.length, | ||
btoaString.length, | ||
btoaString === textDecoder.decode(bufferBase64), | ||
newString === string, | ||
newString === atobString, | ||
newString === textDecoder.decode(bufferFrom), | ||
); |
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
export const tests = [ | ||
// ['', ''], | ||
// ['TWFu', 'Man'], | ||
['QQ==', 'A'], | ||
// ['SGVsbG8gd29ybGQ=', 'Hello world'], | ||
//['SGVsbG8gd29ybGRzIQ==', 'Hello worlds!'], | ||
]; | ||
|
||
export const allBytes = new Uint8Array(256); | ||
for (let i = 0; i < 256; i++) { | ||
allBytes[i] = i; | ||
} | ||
|
||
export const base64AllBytes = | ||
'AAECAwQFBgcICQoLDA0ODxAREhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+P0BBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWltcXV5fYGFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6e3x9fn+AgYKDhIWGh4iJiouMjY6PkJGSk5SVlpeYmZqbnJ2en6ChoqOkpaanqKmqq6ytrq+wsbKztLW2t7i5uru8vb6/wMHCw8TFxsfIycrLzM3Oz9DR0tPU1dbX2Nna29zd3t/g4eLj5OXm5+jp6uvs7e7v8PHy8/T19vf4+fr7/P3+/w=='; |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { decode } from '..'; | ||
|
||
import { tests, allBytes, base64AllBytes } from './data'; | ||
|
||
const textEncoder = new TextEncoder(); | ||
|
||
describe('decode', () => { | ||
it.each(tests)('%s -> %s', (base64: string, binary: string) => { | ||
const encodedBase64 = textEncoder.encode(base64); | ||
const encodedBinary = textEncoder.encode(binary); | ||
expect(Array.from(decode(encodedBase64))).toStrictEqual( | ||
Array.from(encodedBinary), | ||
); | ||
}); | ||
|
||
it('All possibles values', () => { | ||
const encodeBase64 = textEncoder.encode(base64AllBytes); | ||
expect(Array.from(decode(encodeBase64))).toStrictEqual( | ||
Array.from(allBytes), | ||
); | ||
}); | ||
}); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { encode } from '..'; | ||
|
||
import { tests, allBytes, base64AllBytes } from './data'; | ||
|
||
const textEncoder = new TextEncoder(); | ||
|
||
describe('encode', () => { | ||
it.each(tests)('%s -> %s', (base64: string, binary: string) => { | ||
const encodedBinary = textEncoder.encode(binary); | ||
const encodedBase64 = textEncoder.encode(base64); | ||
expect(Array.from(encode(encodedBinary))).toStrictEqual( | ||
Array.from(encodedBase64), | ||
); | ||
}); | ||
|
||
it('All possibles values', () => { | ||
const encodedBase64 = textEncoder.encode(base64AllBytes); | ||
expect(Array.from(encode(allBytes))).toStrictEqual( | ||
Array.from(encodedBase64), | ||
); | ||
}); | ||
}); |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
const base64codes = Uint8Array.from([ | ||
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, | ||
]); | ||
|
||
/** | ||
* Convert a Uint8Array containing a base64 encoded bytes to a Uint8Array containing decoded values | ||
* @returns a Uint8Array containing the decoded bytes | ||
*/ | ||
|
||
export function decode( | ||
input: Uint8Array, //| ArrayBuffer, | ||
): Uint8Array { | ||
if (!ArrayBuffer.isView(input)) { | ||
input = new Uint8Array(input); | ||
} | ||
|
||
if (input.length % 4 !== 0) { | ||
throw new Error('Unable to parse base64 string.'); | ||
} | ||
|
||
let output = new Uint8Array(3 * (input.length / 4)); | ||
if (input.length === 0) return output; | ||
|
||
const missingOctets = | ||
input[input.length - 2] === 61 ? 2 : input[input.length - 1] === 61 ? 1 : 0; | ||
|
||
for (let i = 0, j = 0; i < input.length; i += 4, j += 3) { | ||
const buffer = | ||
(base64codes[input[i]] << 18) | | ||
(base64codes[input[i + 1]] << 12) | | ||
(base64codes[input[i + 2]] << 6) | | ||
base64codes[input[i + 3]]; | ||
output[j] = buffer >> 16; | ||
output[j + 1] = (buffer >> 8) & 0xff; | ||
output[j + 2] = buffer & 0xff; | ||
} | ||
return output.subarray(0, output.length - missingOctets); | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
const base64codes = Uint8Array.from([ | ||
65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, | ||
84, 85, 86, 87, 88, 89, 90, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, | ||
107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, | ||
122, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 43, 47, | ||
]); | ||
|
||
/** | ||
* Convert a Uint8Array containing bytes to a Uint8Array containing the base64 encoded values | ||
* @returns a Uint8Array containing the encoded bytes | ||
*/ | ||
|
||
export function encode(input: Uint8Array): Uint8Array { | ||
const output = new Uint8Array(Math.ceil(input.length / 3) * 4); | ||
let i, j; | ||
for (i = 2, j = 0; i < input.length; i += 3, j += 4) { | ||
output[j] = base64codes[input[i - 2] >> 2]; | ||
output[j + 1] = | ||
base64codes[((input[i - 2] & 0x03) << 4) | (input[i - 1] >> 4)]; | ||
output[j + 2] = base64codes[((input[i - 1] & 0x0f) << 2) | (input[i] >> 6)]; | ||
output[j + 3] = base64codes[input[i] & 0x3f]; | ||
} | ||
if (i === input.length + 1) { | ||
// 1 octet yet to write | ||
output[j] = base64codes[input[i - 2] >> 2]; | ||
output[j + 1] = base64codes[(input[i - 2] & 0x03) << 4]; | ||
output[j + 2] = 61; | ||
output[j + 3] = 61; | ||
} | ||
if (i === input.length) { | ||
// 2 octets yet to write | ||
output[j] = base64codes[input[i - 2] >> 2]; | ||
output[j + 1] = | ||
base64codes[((input[i - 2] & 0x03) << 4) | (input[i - 1] >> 4)]; | ||
output[j + 2] = base64codes[(input[i - 1] & 0x0f) << 2]; | ||
output[j + 3] = 61; | ||
} | ||
return output; | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
export * from './decode'; | ||
export * from './encode'; |