Path: blob/trunk/third_party/closure/goog/crypt/base64.js
2868 views
// Copyright 2007 The Closure Library Authors. All Rights Reserved.1//2// Licensed under the Apache License, Version 2.0 (the "License");3// you may not use this file except in compliance with the License.4// You may obtain a copy of the License at5//6// http://www.apache.org/licenses/LICENSE-2.07//8// Unless required by applicable law or agreed to in writing, software9// distributed under the License is distributed on an "AS-IS" BASIS,10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.11// See the License for the specific language governing permissions and12// limitations under the License.1314/**15* @fileoverview Base64 en/decoding. Not much to say here except that we16* work with decoded values in arrays of bytes. By "byte" I mean a number17* in [0, 255].18*19* @author [email protected] (Gavin Doughtie)20*/2122goog.provide('goog.crypt.base64');2324goog.require('goog.asserts');25goog.require('goog.crypt');26goog.require('goog.string');27goog.require('goog.userAgent');28goog.require('goog.userAgent.product');2930// Static lookup maps, lazily populated by init_()313233/**34* Maps bytes to characters.35* @type {Object}36* @private37*/38goog.crypt.base64.byteToCharMap_ = null;394041/**42* Maps characters to bytes. Used for normal and websafe characters.43* @type {Object}44* @private45*/46goog.crypt.base64.charToByteMap_ = null;474849/**50* Maps bytes to websafe characters.51* @type {Object}52* @private53*/54goog.crypt.base64.byteToCharMapWebSafe_ = null;555657/**58* Our default alphabet, shared between59* ENCODED_VALS and ENCODED_VALS_WEBSAFE60* @type {string}61*/62goog.crypt.base64.ENCODED_VALS_BASE = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' +63'abcdefghijklmnopqrstuvwxyz' +64'0123456789';656667/**68* Our default alphabet. Value 64 (=) is special; it means "nothing."69* @type {string}70*/71goog.crypt.base64.ENCODED_VALS = goog.crypt.base64.ENCODED_VALS_BASE + '+/=';727374/**75* Our websafe alphabet.76* @type {string}77*/78goog.crypt.base64.ENCODED_VALS_WEBSAFE =79goog.crypt.base64.ENCODED_VALS_BASE + '-_.';808182/**83* White list of implementations with known-good native atob and btoa functions.84* Listing these explicitly (via the ASSUME_* wrappers) benefits dead-code85* removal in per-browser compilations.86* @private {boolean}87*/88goog.crypt.base64.ASSUME_NATIVE_SUPPORT_ = goog.userAgent.GECKO ||89(goog.userAgent.WEBKIT && !goog.userAgent.product.SAFARI) ||90goog.userAgent.OPERA;919293/**94* Does this browser have a working btoa function?95* @private {boolean}96*/97goog.crypt.base64.HAS_NATIVE_ENCODE_ =98goog.crypt.base64.ASSUME_NATIVE_SUPPORT_ ||99typeof(goog.global.btoa) == 'function';100101102/**103* Does this browser have a working atob function?104* We blacklist known-bad implementations:105* - IE (10+) added atob() but it does not tolerate whitespace on the input.106* @private {boolean}107*/108goog.crypt.base64.HAS_NATIVE_DECODE_ =109goog.crypt.base64.ASSUME_NATIVE_SUPPORT_ ||110(!goog.userAgent.product.SAFARI && !goog.userAgent.IE &&111typeof(goog.global.atob) == 'function');112113114/**115* Base64-encode an array of bytes.116*117* @param {Array<number>|Uint8Array} input An array of bytes (numbers with118* value in [0, 255]) to encode.119* @param {boolean=} opt_webSafe True indicates we should use the alternative120* alphabet, which does not require escaping for use in URLs.121* @return {string} The base64 encoded string.122*/123goog.crypt.base64.encodeByteArray = function(input, opt_webSafe) {124// Assert avoids runtime dependency on goog.isArrayLike, which helps reduce125// size of jscompiler output, and which yields slight performance increase.126goog.asserts.assert(127goog.isArrayLike(input), 'encodeByteArray takes an array as a parameter');128129goog.crypt.base64.init_();130131var byteToCharMap = opt_webSafe ? goog.crypt.base64.byteToCharMapWebSafe_ :132goog.crypt.base64.byteToCharMap_;133134var output = [];135136for (var i = 0; i < input.length; i += 3) {137var byte1 = input[i];138var haveByte2 = i + 1 < input.length;139var byte2 = haveByte2 ? input[i + 1] : 0;140var haveByte3 = i + 2 < input.length;141var byte3 = haveByte3 ? input[i + 2] : 0;142143var outByte1 = byte1 >> 2;144var outByte2 = ((byte1 & 0x03) << 4) | (byte2 >> 4);145var outByte3 = ((byte2 & 0x0F) << 2) | (byte3 >> 6);146var outByte4 = byte3 & 0x3F;147148if (!haveByte3) {149outByte4 = 64;150151if (!haveByte2) {152outByte3 = 64;153}154}155156output.push(157byteToCharMap[outByte1], byteToCharMap[outByte2],158byteToCharMap[outByte3], byteToCharMap[outByte4]);159}160161return output.join('');162};163164165/**166* Base64-encode a string.167*168* @param {string} input A string to encode.169* @param {boolean=} opt_webSafe True indicates we should use the alternative170* alphabet, which does not require escaping for use in URLs.171* @return {string} The base64 encoded string.172*/173goog.crypt.base64.encodeString = function(input, opt_webSafe) {174// Shortcut for browsers that implement175// a native base64 encoder in the form of "btoa/atob"176if (goog.crypt.base64.HAS_NATIVE_ENCODE_ && !opt_webSafe) {177return goog.global.btoa(input);178}179return goog.crypt.base64.encodeByteArray(180goog.crypt.stringToByteArray(input), opt_webSafe);181};182183184/**185* Base64-decode a string.186*187* @param {string} input Input to decode. Any whitespace is ignored, and the188* input maybe encoded with either supported alphabet (or a mix thereof).189* @param {boolean=} opt_webSafe True indicates we should use the alternative190* alphabet, which does not require escaping for use in URLs. Note that191* passing false may also still allow webSafe input decoding, when the192* fallback decoder is used on browsers without native support.193* @return {string} string representing the decoded value.194*/195goog.crypt.base64.decodeString = function(input, opt_webSafe) {196// Shortcut for browsers that implement197// a native base64 encoder in the form of "btoa/atob"198if (goog.crypt.base64.HAS_NATIVE_DECODE_ && !opt_webSafe) {199return goog.global.atob(input);200}201var output = '';202function pushByte(b) { output += String.fromCharCode(b); }203204goog.crypt.base64.decodeStringInternal_(input, pushByte);205206return output;207};208209210/**211* Base64-decode a string to an Array of numbers.212*213* In base-64 decoding, groups of four characters are converted into three214* bytes. If the encoder did not apply padding, the input length may not215* be a multiple of 4.216*217* In this case, the last group will have fewer than 4 characters, and218* padding will be inferred. If the group has one or two characters, it decodes219* to one byte. If the group has three characters, it decodes to two bytes.220*221* @param {string} input Input to decode. Any whitespace is ignored, and the222* input maybe encoded with either supported alphabet (or a mix thereof).223* @param {boolean=} opt_ignored Unused parameter, retained for compatibility.224* @return {!Array<number>} bytes representing the decoded value.225*/226goog.crypt.base64.decodeStringToByteArray = function(input, opt_ignored) {227var output = [];228function pushByte(b) { output.push(b); }229230goog.crypt.base64.decodeStringInternal_(input, pushByte);231232return output;233};234235236/**237* Base64-decode a string to a Uint8Array.238*239* Note that Uint8Array is not supported on older browsers, e.g. IE < 10.240* @see http://caniuse.com/uint8array241*242* In base-64 decoding, groups of four characters are converted into three243* bytes. If the encoder did not apply padding, the input length may not244* be a multiple of 4.245*246* In this case, the last group will have fewer than 4 characters, and247* padding will be inferred. If the group has one or two characters, it decodes248* to one byte. If the group has three characters, it decodes to two bytes.249*250* @param {string} input Input to decode. Any whitespace is ignored, and the251* input maybe encoded with either supported alphabet (or a mix thereof).252* @return {!Uint8Array} bytes representing the decoded value.253*/254goog.crypt.base64.decodeStringToUint8Array = function(input) {255goog.asserts.assert(256!goog.userAgent.IE || goog.userAgent.isVersionOrHigher('10'),257'Browser does not support typed arrays');258var output = new Uint8Array(Math.ceil(input.length * 3 / 4));259var outLen = 0;260function pushByte(b) { output[outLen++] = b; }261262goog.crypt.base64.decodeStringInternal_(input, pushByte);263264return output.subarray(0, outLen);265};266267268/**269* @param {string} input Input to decode.270* @param {function(number):void} pushByte result accumulator.271* @private272*/273goog.crypt.base64.decodeStringInternal_ = function(input, pushByte) {274goog.crypt.base64.init_();275276var nextCharIndex = 0;277/**278* @param {number} default_val Used for end-of-input.279* @return {number} The next 6-bit value, or the default for end-of-input.280*/281function getByte(default_val) {282while (nextCharIndex < input.length) {283var ch = input.charAt(nextCharIndex++);284var b = goog.crypt.base64.charToByteMap_[ch];285if (b != null) {286return b; // Common case: decoded the char.287}288if (!goog.string.isEmptyOrWhitespace(ch)) {289throw Error('Unknown base64 encoding at char: ' + ch);290}291// We encountered whitespace: loop around to the next input char.292}293return default_val; // No more input remaining.294}295296while (true) {297var byte1 = getByte(-1);298var byte2 = getByte(0);299var byte3 = getByte(64);300var byte4 = getByte(64);301302// The common case is that all four bytes are present, so if we have byte4303// we can skip over the truncated input special case handling.304if (byte4 === 64) {305if (byte1 === -1) {306return; // Terminal case: no input left to decode.307}308// Here we know an intermediate number of bytes are missing.309// The defaults for byte2, byte3 and byte4 apply the inferred padding310// rules per the public API documentation. i.e: 1 byte311// missing should yield 2 bytes of output, but 2 or 3 missing bytes yield312// a single byte of output. (Recall that 64 corresponds the padding char).313}314315var outByte1 = (byte1 << 2) | (byte2 >> 4);316pushByte(outByte1);317318if (byte3 != 64) {319var outByte2 = ((byte2 << 4) & 0xF0) | (byte3 >> 2);320pushByte(outByte2);321322if (byte4 != 64) {323var outByte3 = ((byte3 << 6) & 0xC0) | byte4;324pushByte(outByte3);325}326}327}328};329330331/**332* Lazy static initialization function. Called before333* accessing any of the static map variables.334* @private335*/336goog.crypt.base64.init_ = function() {337if (!goog.crypt.base64.byteToCharMap_) {338goog.crypt.base64.byteToCharMap_ = {};339goog.crypt.base64.charToByteMap_ = {};340goog.crypt.base64.byteToCharMapWebSafe_ = {};341342// We want quick mappings back and forth, so we precompute two maps.343for (var i = 0; i < goog.crypt.base64.ENCODED_VALS.length; i++) {344goog.crypt.base64.byteToCharMap_[i] =345goog.crypt.base64.ENCODED_VALS.charAt(i);346goog.crypt.base64.charToByteMap_[goog.crypt.base64.byteToCharMap_[i]] = i;347goog.crypt.base64.byteToCharMapWebSafe_[i] =348goog.crypt.base64.ENCODED_VALS_WEBSAFE.charAt(i);349350// Be forgiving when decoding and correctly decode both encodings.351if (i >= goog.crypt.base64.ENCODED_VALS_BASE.length) {352goog.crypt.base64353.charToByteMap_[goog.crypt.base64.ENCODED_VALS_WEBSAFE.charAt(i)] =354i;355}356}357}358};359360361