├── .gitignore ├── .npmignore ├── .travis.yml ├── LICENSE ├── README.md ├── base64js.min.js ├── bench ├── basic.js └── bench.js ├── index.d.ts ├── index.js ├── package.json └── test ├── big-data.js ├── convert.js ├── corrupt.js └── url-safe.js /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | npm-debug.log 3 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | .travis.yml 2 | bench/ 3 | test/ 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | os: 3 | - linux 4 | language: node_js 5 | node_js: 6 | - lts/* 7 | - '6' 8 | - '8' 9 | - '10' 10 | - '12' 11 | - '14' 12 | env: 13 | matrix: 14 | - TEST_SUITE=unit 15 | matrix: 16 | include: 17 | - os: linux 18 | node_js: lts/* 19 | env: TEST_SUITE=lint 20 | script: npm run $TEST_SUITE 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Jameson Little 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # base64-js 2 | 3 | ### Ongoing work has temporarily moved from [beatgammit/base64-js](https://github.com/beatgammit/base64-js) to [feross/base64-js](https://github.com/feross/base64-js). 4 | 5 | `base64-js` does basic base64 encoding/decoding in pure JS. 6 | 7 | [![build status](https://secure.travis-ci.org/beatgammit/base64-js.png)](http://travis-ci.org/beatgammit/base64-js) 8 | 9 | Many browsers already have base64 encoding/decoding functionality, but it is for text data, not all-purpose binary data. 10 | 11 | Sometimes encoding/decoding binary data in the browser is useful, and that is what this module does. 12 | 13 | ## install 14 | 15 | With [npm](https://npmjs.org) do: 16 | 17 | `npm install base64-js` and `var base64js = require('base64-js')` 18 | 19 | For use in web browsers do: 20 | 21 | `` 22 | 23 | [Get supported base64-js with the Tidelift Subscription](https://tidelift.com/subscription/pkg/npm-base64-js?utm_source=npm-base64-js&utm_medium=referral&utm_campaign=readme) 24 | 25 | ## methods 26 | 27 | `base64js` has three exposed functions, `byteLength`, `toByteArray` and `fromByteArray`, which both take a single argument. 28 | 29 | - `byteLength` - Takes a base64 string and returns length of byte array 30 | - `toByteArray` - Takes a base64 string and returns a byte array 31 | - `fromByteArray` - Takes a byte array and returns a base64 string 32 | 33 | ## license 34 | 35 | MIT 36 | -------------------------------------------------------------------------------- /base64js.min.js: -------------------------------------------------------------------------------- 1 | (function(a){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b;b="undefined"==typeof window?"undefined"==typeof global?"undefined"==typeof self?this:self:global:window,b.base64js=a()}})(function(){return function(){function b(d,e,g){function a(j,i){if(!e[j]){if(!d[j]){var f="function"==typeof require&&require;if(!i&&f)return f(j,!0);if(h)return h(j,!0);var c=new Error("Cannot find module '"+j+"'");throw c.code="MODULE_NOT_FOUND",c}var k=e[j]={exports:{}};d[j][0].call(k.exports,function(b){var c=d[j][1][b];return a(c||b)},k,k.exports,b,d,e,g)}return e[j].exports}for(var h="function"==typeof require&&require,c=0;c>16,j[k++]=255&b>>8,j[k++]=255&b;return 2===h&&(b=l[a.charCodeAt(c)]<<2|l[a.charCodeAt(c+1)]>>4,j[k++]=255&b),1===h&&(b=l[a.charCodeAt(c)]<<10|l[a.charCodeAt(c+1)]<<4|l[a.charCodeAt(c+2)]>>2,j[k++]=255&b>>8,j[k++]=255&b),j}function g(a){return k[63&a>>18]+k[63&a>>12]+k[63&a>>6]+k[63&a]}function h(a,b,c){for(var d,e=[],f=b;fj?j:g+f));return 1===d?(b=a[c-1],e.push(k[b>>2]+k[63&b<<4]+"==")):2===d&&(b=(a[c-2]<<8)+a[c-1],e.push(k[b>>10]+k[63&b>>4]+k[63&b<<2]+"=")),e.join("")}c.byteLength=function(a){var b=d(a),c=b[0],e=b[1];return 3*(c+e)/4-e},c.toByteArray=f,c.fromByteArray=j;for(var k=[],l=[],m="undefined"==typeof Uint8Array?Array:Uint8Array,n="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",o=0,p=n.length;o 0) { 26 | throw new Error('Invalid string. Length must be a multiple of 4') 27 | } 28 | 29 | // Trim off extra bytes after placeholder bytes are found 30 | // See: https://github.com/beatgammit/base64-js/issues/42 31 | var validLen = b64.indexOf('=') 32 | if (validLen === -1) validLen = len 33 | 34 | var placeHoldersLen = validLen === len 35 | ? 0 36 | : 4 - (validLen % 4) 37 | 38 | return [validLen, placeHoldersLen] 39 | } 40 | 41 | // base64 is 4/3 + up to two characters of the original data 42 | function byteLength (b64) { 43 | var lens = getLens(b64) 44 | var validLen = lens[0] 45 | var placeHoldersLen = lens[1] 46 | return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen 47 | } 48 | 49 | function _byteLength (b64, validLen, placeHoldersLen) { 50 | return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen 51 | } 52 | 53 | function toByteArray (b64) { 54 | var tmp 55 | var lens = getLens(b64) 56 | var validLen = lens[0] 57 | var placeHoldersLen = lens[1] 58 | 59 | var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen)) 60 | 61 | var curByte = 0 62 | 63 | // if there are placeholders, only get up to the last complete 4 chars 64 | var len = placeHoldersLen > 0 65 | ? validLen - 4 66 | : validLen 67 | 68 | var i 69 | for (i = 0; i < len; i += 4) { 70 | tmp = 71 | (revLookup[b64.charCodeAt(i)] << 18) | 72 | (revLookup[b64.charCodeAt(i + 1)] << 12) | 73 | (revLookup[b64.charCodeAt(i + 2)] << 6) | 74 | revLookup[b64.charCodeAt(i + 3)] 75 | arr[curByte++] = (tmp >> 16) & 0xFF 76 | arr[curByte++] = (tmp >> 8) & 0xFF 77 | arr[curByte++] = tmp & 0xFF 78 | } 79 | 80 | if (placeHoldersLen === 2) { 81 | tmp = 82 | (revLookup[b64.charCodeAt(i)] << 2) | 83 | (revLookup[b64.charCodeAt(i + 1)] >> 4) 84 | arr[curByte++] = tmp & 0xFF 85 | } 86 | 87 | if (placeHoldersLen === 1) { 88 | tmp = 89 | (revLookup[b64.charCodeAt(i)] << 10) | 90 | (revLookup[b64.charCodeAt(i + 1)] << 4) | 91 | (revLookup[b64.charCodeAt(i + 2)] >> 2) 92 | arr[curByte++] = (tmp >> 8) & 0xFF 93 | arr[curByte++] = tmp & 0xFF 94 | } 95 | 96 | return arr 97 | } 98 | 99 | function tripletToBase64 (num) { 100 | return lookup[num >> 18 & 0x3F] + 101 | lookup[num >> 12 & 0x3F] + 102 | lookup[num >> 6 & 0x3F] + 103 | lookup[num & 0x3F] 104 | } 105 | 106 | function encodeChunk (uint8, start, end) { 107 | var tmp 108 | var output = [] 109 | for (var i = start; i < end; i += 3) { 110 | tmp = 111 | ((uint8[i] << 16) & 0xFF0000) + 112 | ((uint8[i + 1] << 8) & 0xFF00) + 113 | (uint8[i + 2] & 0xFF) 114 | output.push(tripletToBase64(tmp)) 115 | } 116 | return output.join('') 117 | } 118 | 119 | function fromByteArray (uint8) { 120 | var tmp 121 | var len = uint8.length 122 | var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes 123 | var parts = [] 124 | var maxChunkLength = 16383 // must be multiple of 3 125 | 126 | // go through the array every three bytes, we'll deal with trailing stuff later 127 | for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) { 128 | parts.push(encodeChunk(uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength))) 129 | } 130 | 131 | // pad the end with zeros, but make sure to not forget the extra bytes 132 | if (extraBytes === 1) { 133 | tmp = uint8[len - 1] 134 | parts.push( 135 | lookup[tmp >> 2] + 136 | lookup[(tmp << 4) & 0x3F] + 137 | '==' 138 | ) 139 | } else if (extraBytes === 2) { 140 | tmp = (uint8[len - 2] << 8) + uint8[len - 1] 141 | parts.push( 142 | lookup[tmp >> 10] + 143 | lookup[(tmp >> 4) & 0x3F] + 144 | lookup[(tmp << 2) & 0x3F] + 145 | '=' 146 | ) 147 | } 148 | 149 | return parts.join('') 150 | } 151 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "base64-js", 3 | "description": "Base64 encoding/decoding in pure JS", 4 | "version": "1.5.1", 5 | "author": "T. Jameson Little ", 6 | "typings": "index.d.ts", 7 | "bugs": { 8 | "url": "https://github.com/beatgammit/base64-js/issues" 9 | }, 10 | "devDependencies": { 11 | "babel-minify": "^0.5.1", 12 | "benchmark": "^2.1.4", 13 | "browserify": "^16.3.0", 14 | "standard": "*", 15 | "tape": "4.x" 16 | }, 17 | "homepage": "https://github.com/beatgammit/base64-js", 18 | "keywords": [ 19 | "base64" 20 | ], 21 | "license": "MIT", 22 | "main": "index.js", 23 | "repository": { 24 | "type": "git", 25 | "url": "git://github.com/beatgammit/base64-js.git" 26 | }, 27 | "scripts": { 28 | "build": "browserify -s base64js -r ./ | minify > base64js.min.js", 29 | "lint": "standard", 30 | "test": "npm run lint && npm run unit", 31 | "unit": "tape test/*.js" 32 | }, 33 | "funding": [ 34 | { 35 | "type": "github", 36 | "url": "https://github.com/sponsors/feross" 37 | }, 38 | { 39 | "type": "patreon", 40 | "url": "https://www.patreon.com/feross" 41 | }, 42 | { 43 | "type": "consulting", 44 | "url": "https://feross.org/support" 45 | } 46 | ] 47 | } 48 | -------------------------------------------------------------------------------- /test/big-data.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const b64 = require('../') 3 | 4 | test('convert big data to base64', function (t) { 5 | const big = new Uint8Array(64 * 1024 * 1024) 6 | for (let i = 0, length = big.length; i < length; ++i) { 7 | big[i] = i % 256 8 | } 9 | const b64str = b64.fromByteArray(big) 10 | const arr = b64.toByteArray(b64str) 11 | t.ok(equal(arr, big)) 12 | t.equal(b64.byteLength(b64str), arr.length) 13 | t.end() 14 | }) 15 | 16 | function equal (a, b) { 17 | let i 18 | const length = a.length 19 | if (length !== b.length) return false 20 | for (i = 0; i < length; ++i) { 21 | if (a[i] !== b[i]) return false 22 | } 23 | return true 24 | } 25 | -------------------------------------------------------------------------------- /test/convert.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const b64 = require('../') 3 | const checks = [ 4 | 'a', 5 | 'aa', 6 | 'aaa', 7 | 'hi', 8 | 'hi!', 9 | 'hi!!', 10 | 'sup', 11 | 'sup?', 12 | 'sup?!' 13 | ] 14 | 15 | test('convert to base64 and back', function (t) { 16 | t.plan(checks.length * 2) 17 | 18 | for (let i = 0; i < checks.length; i++) { 19 | const check = checks[i] 20 | 21 | const b64Str = b64.fromByteArray(map(check, function (char) { return char.charCodeAt(0) })) 22 | 23 | const arr = b64.toByteArray(b64Str) 24 | const str = map(arr, function (byte) { return String.fromCharCode(byte) }).join('') 25 | 26 | t.equal(check, str, 'Checked ' + check) 27 | t.equal(b64.byteLength(b64Str), arr.length, 'Checked length for ' + check) 28 | } 29 | }) 30 | 31 | const data = [ 32 | [[0, 0, 0], 'AAAA'], 33 | [[0, 0, 1], 'AAAB'], 34 | [[0, 1, -1], 'AAH/'], 35 | [[1, 1, 1], 'AQEB'], 36 | [[0, -73, 23], 'ALcX'] 37 | ] 38 | 39 | test('convert known data to string', function (t) { 40 | for (let i = 0; i < data.length; i++) { 41 | const bytes = data[i][0] 42 | const expected = data[i][1] 43 | const actual = b64.fromByteArray(bytes) 44 | t.equal(actual, expected, 'Ensure that ' + bytes + ' serialise to ' + expected) 45 | } 46 | t.end() 47 | }) 48 | 49 | test('convert known data from string', function (t) { 50 | for (let i = 0; i < data.length; i++) { 51 | const expected = data[i][0] 52 | const string = data[i][1] 53 | const actual = b64.toByteArray(string) 54 | t.ok(equal(actual, expected), 'Ensure that ' + string + ' deserialise to ' + expected) 55 | const length = b64.byteLength(string) 56 | t.equal(length, expected.length, 'Ensure that ' + string + ' has byte lentgh of ' + expected.length) 57 | } 58 | t.end() 59 | }) 60 | 61 | function equal (a, b) { 62 | let i 63 | const length = a.length 64 | if (length !== b.length) return false 65 | for (i = 0; i < length; ++i) { 66 | if ((a[i] & 0xFF) !== (b[i] & 0xFF)) return false 67 | } 68 | return true 69 | } 70 | 71 | function map (arr, callback) { 72 | const res = [] 73 | let kValue, mappedValue 74 | 75 | for (let k = 0, len = arr.length; k < len; k++) { 76 | if ((typeof arr === 'string' && !!arr.charAt(k))) { 77 | kValue = arr.charAt(k) 78 | mappedValue = callback(kValue, k, arr) 79 | res[k] = mappedValue 80 | } else if (typeof arr !== 'string' && k in arr) { 81 | kValue = arr[k] 82 | mappedValue = callback(kValue, k, arr) 83 | res[k] = mappedValue 84 | } 85 | } 86 | return res 87 | } 88 | -------------------------------------------------------------------------------- /test/corrupt.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const b64 = require('../') 3 | 4 | test('padding bytes found inside base64 string', function (t) { 5 | // See https://github.com/beatgammit/base64-js/issues/42 6 | const str = 'SQ==QU0=' 7 | t.deepEqual(b64.toByteArray(str), new Uint8Array([73])) 8 | t.equal(b64.byteLength(str), 1) 9 | t.end() 10 | }) 11 | -------------------------------------------------------------------------------- /test/url-safe.js: -------------------------------------------------------------------------------- 1 | const test = require('tape') 2 | const b64 = require('../') 3 | 4 | test('decode url-safe style base64 strings', function (t) { 5 | const expected = [0xff, 0xff, 0xbe, 0xff, 0xef, 0xbf, 0xfb, 0xef, 0xff] 6 | 7 | let str = '//++/++/++//' 8 | let actual = b64.toByteArray(str) 9 | for (let i = 0; i < actual.length; i++) { 10 | t.equal(actual[i], expected[i]) 11 | } 12 | 13 | t.equal(b64.byteLength(str), actual.length) 14 | 15 | str = '__--_--_--__' 16 | actual = b64.toByteArray(str) 17 | for (let i = 0; i < actual.length; i++) { 18 | t.equal(actual[i], expected[i]) 19 | } 20 | 21 | t.equal(b64.byteLength(str), actual.length) 22 | 23 | t.end() 24 | }) 25 | --------------------------------------------------------------------------------