123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- 'use strict';
- var globalThis = require('../internals/global-this');
- var uncurryThis = require('../internals/function-uncurry-this');
- var anObjectOrUndefined = require('../internals/an-object-or-undefined');
- var aString = require('../internals/a-string');
- var hasOwn = require('../internals/has-own-property');
- var base64Map = require('../internals/base64-map');
- var getAlphabetOption = require('../internals/get-alphabet-option');
- var notDetached = require('../internals/array-buffer-not-detached');
-
- var base64Alphabet = base64Map.c2i;
- var base64UrlAlphabet = base64Map.c2iUrl;
-
- var SyntaxError = globalThis.SyntaxError;
- var TypeError = globalThis.TypeError;
- var at = uncurryThis(''.charAt);
-
- var skipAsciiWhitespace = function (string, index) {
- var length = string.length;
- for (;index < length; index++) {
- var chr = at(string, index);
- if (chr !== ' ' && chr !== '\t' && chr !== '\n' && chr !== '\f' && chr !== '\r') break;
- } return index;
- };
-
- var decodeBase64Chunk = function (chunk, alphabet, throwOnExtraBits) {
- var chunkLength = chunk.length;
-
- if (chunkLength < 4) {
- chunk += chunkLength === 2 ? 'AA' : 'A';
- }
-
- var triplet = (alphabet[at(chunk, 0)] << 18)
- + (alphabet[at(chunk, 1)] << 12)
- + (alphabet[at(chunk, 2)] << 6)
- + alphabet[at(chunk, 3)];
-
- var chunkBytes = [
- (triplet >> 16) & 255,
- (triplet >> 8) & 255,
- triplet & 255
- ];
-
- if (chunkLength === 2) {
- if (throwOnExtraBits && chunkBytes[1] !== 0) {
- throw new SyntaxError('Extra bits');
- }
- return [chunkBytes[0]];
- }
-
- if (chunkLength === 3) {
- if (throwOnExtraBits && chunkBytes[2] !== 0) {
- throw new SyntaxError('Extra bits');
- }
- return [chunkBytes[0], chunkBytes[1]];
- }
-
- return chunkBytes;
- };
-
- var writeBytes = function (bytes, elements, written) {
- var elementsLength = elements.length;
- for (var index = 0; index < elementsLength; index++) {
- bytes[written + index] = elements[index];
- }
- return written + elementsLength;
- };
-
- /* eslint-disable max-statements, max-depth -- TODO */
- module.exports = function (string, options, into, maxLength) {
- aString(string);
- anObjectOrUndefined(options);
- var alphabet = getAlphabetOption(options) === 'base64' ? base64Alphabet : base64UrlAlphabet;
- var lastChunkHandling = options ? options.lastChunkHandling : undefined;
-
- if (lastChunkHandling === undefined) lastChunkHandling = 'loose';
-
- if (lastChunkHandling !== 'loose' && lastChunkHandling !== 'strict' && lastChunkHandling !== 'stop-before-partial') {
- throw new TypeError('Incorrect `lastChunkHandling` option');
- }
-
- if (into) notDetached(into.buffer);
-
- var bytes = into || [];
- var written = 0;
- var read = 0;
- var chunk = '';
- var index = 0;
-
- if (maxLength) while (true) {
- index = skipAsciiWhitespace(string, index);
- if (index === string.length) {
- if (chunk.length > 0) {
- if (lastChunkHandling === 'stop-before-partial') {
- break;
- }
- if (lastChunkHandling === 'loose') {
- if (chunk.length === 1) {
- throw new SyntaxError('Malformed padding: exactly one additional character');
- }
- written = writeBytes(bytes, decodeBase64Chunk(chunk, alphabet, false), written);
- } else {
- throw new SyntaxError('Missing padding');
- }
- }
- read = string.length;
- break;
- }
- var chr = at(string, index);
- ++index;
- if (chr === '=') {
- if (chunk.length < 2) {
- throw new SyntaxError('Padding is too early');
- }
- index = skipAsciiWhitespace(string, index);
- if (chunk.length === 2) {
- if (index === string.length) {
- if (lastChunkHandling === 'stop-before-partial') {
- break;
- }
- throw new SyntaxError('Malformed padding: only one =');
- }
- if (at(string, index) === '=') {
- ++index;
- index = skipAsciiWhitespace(string, index);
- }
- }
- if (index < string.length) {
- throw new SyntaxError('Unexpected character after padding');
- }
- written = writeBytes(bytes, decodeBase64Chunk(chunk, alphabet, lastChunkHandling === 'strict'), written);
- read = string.length;
- break;
- }
- if (!hasOwn(alphabet, chr)) {
- throw new SyntaxError('Unexpected character');
- }
- var remainingBytes = maxLength - written;
- if (remainingBytes === 1 && chunk.length === 2 || remainingBytes === 2 && chunk.length === 3) {
- // special case: we can fit exactly the number of bytes currently represented by chunk, so we were just checking for `=`
- break;
- }
-
- chunk += chr;
- if (chunk.length === 4) {
- written = writeBytes(bytes, decodeBase64Chunk(chunk, alphabet, false), written);
- chunk = '';
- read = index;
- if (written === maxLength) {
- break;
- }
- }
- }
-
- return { bytes: bytes, read: read, written: written };
- };
|