[79a0317] | 1 | 'use strict';
|
---|
| 2 | var globalThis = require('../internals/global-this');
|
---|
| 3 | var uncurryThis = require('../internals/function-uncurry-this');
|
---|
| 4 | var anObjectOrUndefined = require('../internals/an-object-or-undefined');
|
---|
| 5 | var aString = require('../internals/a-string');
|
---|
| 6 | var hasOwn = require('../internals/has-own-property');
|
---|
| 7 | var base64Map = require('../internals/base64-map');
|
---|
| 8 | var getAlphabetOption = require('../internals/get-alphabet-option');
|
---|
| 9 | var notDetached = require('../internals/array-buffer-not-detached');
|
---|
| 10 |
|
---|
| 11 | var base64Alphabet = base64Map.c2i;
|
---|
| 12 | var base64UrlAlphabet = base64Map.c2iUrl;
|
---|
| 13 |
|
---|
| 14 | var SyntaxError = globalThis.SyntaxError;
|
---|
| 15 | var TypeError = globalThis.TypeError;
|
---|
| 16 | var at = uncurryThis(''.charAt);
|
---|
| 17 |
|
---|
| 18 | var skipAsciiWhitespace = function (string, index) {
|
---|
| 19 | var length = string.length;
|
---|
| 20 | for (;index < length; index++) {
|
---|
| 21 | var chr = at(string, index);
|
---|
| 22 | if (chr !== ' ' && chr !== '\t' && chr !== '\n' && chr !== '\f' && chr !== '\r') break;
|
---|
| 23 | } return index;
|
---|
| 24 | };
|
---|
| 25 |
|
---|
| 26 | var decodeBase64Chunk = function (chunk, alphabet, throwOnExtraBits) {
|
---|
| 27 | var chunkLength = chunk.length;
|
---|
| 28 |
|
---|
| 29 | if (chunkLength < 4) {
|
---|
| 30 | chunk += chunkLength === 2 ? 'AA' : 'A';
|
---|
| 31 | }
|
---|
| 32 |
|
---|
| 33 | var triplet = (alphabet[at(chunk, 0)] << 18)
|
---|
| 34 | + (alphabet[at(chunk, 1)] << 12)
|
---|
| 35 | + (alphabet[at(chunk, 2)] << 6)
|
---|
| 36 | + alphabet[at(chunk, 3)];
|
---|
| 37 |
|
---|
| 38 | var chunkBytes = [
|
---|
| 39 | (triplet >> 16) & 255,
|
---|
| 40 | (triplet >> 8) & 255,
|
---|
| 41 | triplet & 255
|
---|
| 42 | ];
|
---|
| 43 |
|
---|
| 44 | if (chunkLength === 2) {
|
---|
| 45 | if (throwOnExtraBits && chunkBytes[1] !== 0) {
|
---|
| 46 | throw new SyntaxError('Extra bits');
|
---|
| 47 | }
|
---|
| 48 | return [chunkBytes[0]];
|
---|
| 49 | }
|
---|
| 50 |
|
---|
| 51 | if (chunkLength === 3) {
|
---|
| 52 | if (throwOnExtraBits && chunkBytes[2] !== 0) {
|
---|
| 53 | throw new SyntaxError('Extra bits');
|
---|
| 54 | }
|
---|
| 55 | return [chunkBytes[0], chunkBytes[1]];
|
---|
| 56 | }
|
---|
| 57 |
|
---|
| 58 | return chunkBytes;
|
---|
| 59 | };
|
---|
| 60 |
|
---|
| 61 | var writeBytes = function (bytes, elements, written) {
|
---|
| 62 | var elementsLength = elements.length;
|
---|
| 63 | for (var index = 0; index < elementsLength; index++) {
|
---|
| 64 | bytes[written + index] = elements[index];
|
---|
| 65 | }
|
---|
| 66 | return written + elementsLength;
|
---|
| 67 | };
|
---|
| 68 |
|
---|
| 69 | /* eslint-disable max-statements, max-depth -- TODO */
|
---|
| 70 | module.exports = function (string, options, into, maxLength) {
|
---|
| 71 | aString(string);
|
---|
| 72 | anObjectOrUndefined(options);
|
---|
| 73 | var alphabet = getAlphabetOption(options) === 'base64' ? base64Alphabet : base64UrlAlphabet;
|
---|
| 74 | var lastChunkHandling = options ? options.lastChunkHandling : undefined;
|
---|
| 75 |
|
---|
| 76 | if (lastChunkHandling === undefined) lastChunkHandling = 'loose';
|
---|
| 77 |
|
---|
| 78 | if (lastChunkHandling !== 'loose' && lastChunkHandling !== 'strict' && lastChunkHandling !== 'stop-before-partial') {
|
---|
| 79 | throw new TypeError('Incorrect `lastChunkHandling` option');
|
---|
| 80 | }
|
---|
| 81 |
|
---|
| 82 | if (into) notDetached(into.buffer);
|
---|
| 83 |
|
---|
| 84 | var bytes = into || [];
|
---|
| 85 | var written = 0;
|
---|
| 86 | var read = 0;
|
---|
| 87 | var chunk = '';
|
---|
| 88 | var index = 0;
|
---|
| 89 |
|
---|
| 90 | if (maxLength) while (true) {
|
---|
| 91 | index = skipAsciiWhitespace(string, index);
|
---|
| 92 | if (index === string.length) {
|
---|
| 93 | if (chunk.length > 0) {
|
---|
| 94 | if (lastChunkHandling === 'stop-before-partial') {
|
---|
| 95 | break;
|
---|
| 96 | }
|
---|
| 97 | if (lastChunkHandling === 'loose') {
|
---|
| 98 | if (chunk.length === 1) {
|
---|
| 99 | throw new SyntaxError('Malformed padding: exactly one additional character');
|
---|
| 100 | }
|
---|
| 101 | written = writeBytes(bytes, decodeBase64Chunk(chunk, alphabet, false), written);
|
---|
| 102 | } else {
|
---|
| 103 | throw new SyntaxError('Missing padding');
|
---|
| 104 | }
|
---|
| 105 | }
|
---|
| 106 | read = string.length;
|
---|
| 107 | break;
|
---|
| 108 | }
|
---|
| 109 | var chr = at(string, index);
|
---|
| 110 | ++index;
|
---|
| 111 | if (chr === '=') {
|
---|
| 112 | if (chunk.length < 2) {
|
---|
| 113 | throw new SyntaxError('Padding is too early');
|
---|
| 114 | }
|
---|
| 115 | index = skipAsciiWhitespace(string, index);
|
---|
| 116 | if (chunk.length === 2) {
|
---|
| 117 | if (index === string.length) {
|
---|
| 118 | if (lastChunkHandling === 'stop-before-partial') {
|
---|
| 119 | break;
|
---|
| 120 | }
|
---|
| 121 | throw new SyntaxError('Malformed padding: only one =');
|
---|
| 122 | }
|
---|
| 123 | if (at(string, index) === '=') {
|
---|
| 124 | ++index;
|
---|
| 125 | index = skipAsciiWhitespace(string, index);
|
---|
| 126 | }
|
---|
| 127 | }
|
---|
| 128 | if (index < string.length) {
|
---|
| 129 | throw new SyntaxError('Unexpected character after padding');
|
---|
| 130 | }
|
---|
| 131 | written = writeBytes(bytes, decodeBase64Chunk(chunk, alphabet, lastChunkHandling === 'strict'), written);
|
---|
| 132 | read = string.length;
|
---|
| 133 | break;
|
---|
| 134 | }
|
---|
| 135 | if (!hasOwn(alphabet, chr)) {
|
---|
| 136 | throw new SyntaxError('Unexpected character');
|
---|
| 137 | }
|
---|
| 138 | var remainingBytes = maxLength - written;
|
---|
| 139 | if (remainingBytes === 1 && chunk.length === 2 || remainingBytes === 2 && chunk.length === 3) {
|
---|
| 140 | // special case: we can fit exactly the number of bytes currently represented by chunk, so we were just checking for `=`
|
---|
| 141 | break;
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | chunk += chr;
|
---|
| 145 | if (chunk.length === 4) {
|
---|
| 146 | written = writeBytes(bytes, decodeBase64Chunk(chunk, alphabet, false), written);
|
---|
| 147 | chunk = '';
|
---|
| 148 | read = index;
|
---|
| 149 | if (written === maxLength) {
|
---|
| 150 | break;
|
---|
| 151 | }
|
---|
| 152 | }
|
---|
| 153 | }
|
---|
| 154 |
|
---|
| 155 | return { bytes: bytes, read: read, written: written };
|
---|
| 156 | };
|
---|