j?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 |
--------------------------------------------------------------------------------