Data Encoding Tutorials - Herong's Tutorial Examples - v5.23, by Herong Yang
Goetz' Implementation of Base64 in JavaScript
This section provides a JavaScript implementation of the Base64 encoding algorithm by Goetz Heller.
If you are trying to implement Base64 encoding in JavaScript, you can look at the source code developed by Goetz Heller, at http://hellerim.net/base64_src.php.
/// // This file implements base64 encoding and decoding. // Encoding is done by the function base64Encode(), decoding // by base64Decode(). The naming mimics closely the corresponding // library functions found in PHP. However, this implementation allows // for a more flexible use. // // This implementation follows RFC 3548 (http://www.faqs.org/rfcs/rfc3548.html), // so the copyright formulated therein applies. // // Dr.Heller Information Management, 2005 (http://www.hellerim.de). /// // Commented out - part 1 start /* try { if (system.utility.base64 == null) { throw new Error(''); }; } catch(e) { // don't install twice // dependencies try { eval('core'); } catch(e) { throw new Error('base64: class \'core\' not installed'); } */ // Commented out - part 1 end var base64 = function(){}; // provide for class information base64.classID = function() { return 'system.utility.base64'; }; //disallow subclassing base64.isFinal = function() { return true; }; // original base64 encoding base64.encString = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'; // URL and file name safe encoding base64.encStringS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'; /// BEGIN_DOC(base64).METHOD(encode) /// // method String base64.encode(INPUTTYPE inp [, bool uc [, bool safe]]) // // Encode input data into a base64 character string. // // Function arguments: // INPUTTYPE inp: data to be encoded. INPUTTYPE may be String or Array. // Any other INPUTTYPE results in an output value of null. // If INPUTTYPE is String each character is converted into // two bytes each of which is encoded separately. // bool uc: Optional. If this parameter has a value of 'true' which is // the default, code of each character is treated as a 16-bit // entity (UniCode), i.e. as two bytes. Otherwise, the codes // are truncated to one byte (8-bit character set) which // may result in information loss. If INPUTTYPE is Array, // the value of this parameter has no effect. // bool safe: Optional. If this parameter is set to true, the standard // base64 character set is replaced with a modified version where // the characters '+' and '/' are replace with '-' and '_', // respectively, in order to avoid problems with file system // namings which otherwise could occur on some systems. // By default, the value of this argument is assumed to be // false. // Return value: The function returns a character string consisting of // the base64 representation of the input. Its length is a // multiple of 4. If the encoding yields less than this // the string is stuffed with the '=' character. In each case, // the string maybe empty but not null if no error occurred. // Errors: Whenever an error occurs, null is returned. Parameter values // not defined above are considered errors. // Remarks: If the input array contains something different from a // byte at some position the first 8 bits only of this entity are // processed silently without returning an error, which probably // results in garbage converted to base64. // /// END_DOC base64.encode = function(inp, uc, safe) { // do some argument checking if (arguments.length < 1) return null; var readBuf = new Array(); // read buffer if (arguments.length >= 3 && safe != true && safe != false) return null; var enc = (arguments.length >= 3 && safe) ? this.encStringS : this.encString; // character set used var b = (typeof inp == "string"); // how input is to be processed if (!b && (typeof inp != "object") && !(inp instanceof Array)) return null; // bad input if (arguments.length < 2) { uc = true; // set default } // otherwise its value is passed from the caller if (uc != true && uc != false) return null; var n = (!b || !uc) ? 1 : 2; // length of read buffer var out = ''; // output string var c = 0; // holds character code (maybe 16 bit or 8 bit) var j = 1; // sextett counter var l = 0; // work buffer var s = 0; // holds sextett // convert for (var i = 0; i < inp.length; i++) { // read input c = (b) ? inp.charCodeAt(i) : inp[i]; // fill read buffer for (var k = n - 1; k >= 0; k--) { readBuf[k] = c & 0xff; c >>= 8; } for (var m = 0; m < n; m++) { // run through read buffer // process bytes from read buffer l = ((l<<8)&0xff00) | readBuf[m]; // shift remaining bits one byte to the // left and append next byte s = (0x3f<<(2*j)) & l; // extract sextett from buffer l -=s; // remove those bits from buffer; out += enc.charAt(s>>(2*j)); // convert leftmost sextett and append // it to output j++; if (j==4) { // another sextett is complete out += enc.charAt(l&0x3f); // convert and append it j = 1; } } } switch (j) { // handle left-over sextetts case 2: s = 0x3f & (16 * l); // extract sextett from buffer out += enc.charAt(s); // convert leftmost sextett and append // it to output out += '=='; // stuff break; case 3: s = 0x3f & (4 * l); // extract sextett from buffer out += enc.charAt(s); // convert leftmost sextett and append // it to output out += '='; // stuff break; default: break; } return out; } /// BEGIN_DOC(base64).METHOD(decode) /// // method RETURNTYPE base64.decode(String inp [, enum outType [, bool safe // [, bool lax]]]) // // Encode input data into a base64 character string. // // Function arguments: // String inp: base64 encoded data string to be decoded. // enum outType: Optional. This parameter specifies the type of the output and // determines how the input data is to be interpreted.: // 0 - binary data; create a byte array (default) // 1 - 8-bit character string, assuming 1-byte characters // encoded in inp // 2 - 16-bit (UniCode) character string, assuming 2-byte // characters encoded in inp // If 2 is passed to the function, but the number of base64 // characters is odd, a value of null is returned. // bool safe: Optional. If this parameter is set to true, the standard base64 // character set is replaced with a modified version where // the characters '+' and '/' are replaced with '-' and '_', // repectively, in order to avoid problems with file system // namings which otherwise could occur on some systems. // By default, the value of this argument is assumed to be // false. // bool lax: Optional. If set to true, the function skips all input // characters which cannot be processed. This includes the // character '=', too, if it is followed by at least one different // character before the string ends. However, if skipping // infeasible characters amounts to a number // of allowed base64 characters which is not a multiple of 4, // this is considered an error and null is returned. // If lax is set to false (the default), null is returned // whenever an infeasible character is found. // The purpose of this parameter is to give support in cases // where data has been base64 encoded and later on was folded by // some other software, e.g. '\r\n\'s have been inserted in email. // exchange. // Return value: The function's processing result value is stored in a string or // in a byte array before it is returned, depending on the value // assigned to the type parameter. In each case, the value // maybe empty but not null if no error occurred. // Errors: Whenever an error occurs, null is returned. Parameter values // not defined above are considered errors. // /// END_DOC base64.decode = function(inp, outType, safe, lax) { // do some argument checking if (arguments.length < 1) return null; if (arguments.length < 2) outType = 0 ;// produce character array by default if (outType != 0 && outType != 1 && outType != 2) return null; if (arguments.length >= 3 && safe != true && safe != false) return null; var sEnc = (arguments.length >= 3 && safe) ? this.encStringS : this.encString; // select encoding character set if (arguments.length >= 4 && lax != true && lax != false) return null; var aDec = new Object(); // create an associative array for decoding for (var p = 0; p < sEnc.length; p++) { // populate array aDec[sEnc.charAt(p)] = p; } var out = (outType == 0) ? new Array() : ''; lax = (arguments.length == 4 && lax); // ignore non-base64 characters var l = 0; // work area var i = 0; // index into input var j = 0; // sextett counter var c = 0; // input buffer var k = 0; // index into work area var end = inp.length; // one position past the last character to be processed var C = ''; // check input if (lax) { var inpS = ''; // shadow input var ignore = false; // determines wether '=' must be counted var cnt = 0; for (var p = 1; p <= inp.length; p++) { // check and cleanup string before trying to decode c = inp.charAt(end - p); if (c == '=') { if (!ignore) { if (++cnt > 1) ignore = true; } else { continue; } } else if (undefined != aDec[c]) { // the character is base64, hence feasible if (!ignore) ignore = true; // no more '=' allowed inpS = c + inpS; // prepend c to shadow input } } for (var p = 0; p <= cnt; p++) { // at most cnt '=''s were garbage, a number if (p == 2) return null; // in [inpS.length, inpS.length + cnt] must if ((inpS.length + cnt)%4 == 0) break; // be a multiple of 4 } if (inpS.length%4==1) return null; // must be 0, 2, or 3 for inpS to contain // correctly base64 encoded data inp = inpS; // inp now contains feasible characters only end = inp.length; } else { if (inp.length%4 > 0) return null; // invalid length for (var p = 0; p < 2; p++) { // search for trailing '=''s if (inp.charAt(end - 1) == '=') { end--; } else { break; } } } // convert for (i = 0; i < end; i++) { l <<= 6; // clear space for next sextett if (undefined == (c = aDec[inp.charAt(i)])) return null; // lax must be false at this place! l |= (c&0x3f); // append it if (j == 0) { j++; continue; // work area contains incomplete byte only } if (outType == 2) { if (k == 1) { // work area contains complete double byte out += String.fromCharCode(l>>(2*(3-j))); // convert leftmost 16 bits and append them to string l &= ~(0xffff<<(2*(3-j))); // clear the 16 processed bits } k = ++k%2; } else { // work area contains complete byte if (outType == 0) { out.push(l>>(2*(3-j))); // append byte to array } else { out += String.fromCharCode(l>>(2*(3-j))); // convert leftmost 8 bits and append them to String } l &= ~(0xff<<(2*(3-j))); // clear the 8 processed bits } j = ++j%4; // increment sextett counter cyclically } if (outType == 2 && k == 1) return null; // incomplete double byte in work area return out; } // Commented out - part 2 start /* core.installClass(base64); } // end catch */ // Commented out - part 2 start
See the next section for a testing program and test results.
Table of Contents
RFC 1421 - Privacy Enhancement for Email
RFC 1521 - MIME (Multipurpose Internet Mail Extensions)
W3C Implementation of Base64 in Java
Sun Implementation of Base64 in Java
Sun Implementation of Base64 in Java - Test
►Goetz' Implementation of Base64 in JavaScript
Goetz' Implementation of Base64 in JavaScript - Test
Base64 Encoding and Decoding Tools
Base64URL - URL Safe Base64 Encoding