btoa vs third party base64 encoder

JavaScript performance comparison

Test case created by MRis

Info

Encoding large data in base64 can be slow. Also, native implementations of btoa do not correctly handle utf-8 characters. Searching for the best performing alternative.

Preparation code

 
<script>
Benchmark.prototype.setup = function() {
    /**
    *
    *  Base64 encode / decode
    *  http://www.webtoolkit.info/
    *
    **/

     
    Base64 = {
     
        // private property
        _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",
     
        // public method for encoding
        encode : function (input) {
                var output = "";
                var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
                var i = 0;
     
                input = Base64._utf8_encode(input);
     
                while (i < input.length) {
     
                        chr1 = input.charCodeAt(i++);
                        chr2 = input.charCodeAt(i++);
                        chr3 = input.charCodeAt(i++);
     
                        enc1 = chr1 >> 2;
                        enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                        enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                        enc4 = chr3 & 63;
     
                        if (isNaN(chr2)) {
                                enc3 = enc4 = 64;
                        } else if (isNaN(chr3)) {
                                enc4 = 64;
                        }
     
                        output = output +
                        this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
                        this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
     
                }
     
                return output;
        },
     
        // public method for decoding
        decode : function (input) {
                var output = "";
                var chr1, chr2, chr3;
                var enc1, enc2, enc3, enc4;
                var i = 0;
     
                input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
     
                while (i < input.length) {
     
                        enc1 = this._keyStr.indexOf(input.charAt(i++));
                        enc2 = this._keyStr.indexOf(input.charAt(i++));
                        enc3 = this._keyStr.indexOf(input.charAt(i++));
                        enc4 = this._keyStr.indexOf(input.charAt(i++));
     
                        chr1 = (enc1 << 2) | (enc2 >> 4);
                        chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                        chr3 = ((enc3 & 3) << 6) | enc4;
     
                        output = output + String.fromCharCode(chr1);
     
                        if (enc3 != 64) {
                                output = output + String.fromCharCode(chr2);
                        }
                        if (enc4 != 64) {
                                output = output + String.fromCharCode(chr3);
                        }
     
                }
     
                output = Base64._utf8_decode(output);
     
                return output;
     
        },
     
        // private method for UTF-8 encoding
        _utf8_encode : function (string) {
                string = string.replace(/\r\n/g,"\n");
                var utftext = "";
     
                for (var n = 0; n < string.length; n++) {
     
                        var c = string.charCodeAt(n);
     
                        if (c < 128) {
                                utftext += String.fromCharCode(c);
                        }
                        else if((c > 127) && (c < 2048)) {
                                utftext += String.fromCharCode((c >> 6) | 192);
                                utftext += String.fromCharCode((c & 63) | 128);
                        }
                        else {
                                utftext += String.fromCharCode((c >> 12) | 224);
                                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                                utftext += String.fromCharCode((c & 63) | 128);
                        }
     
                }
     
                return utftext;
        },
     
        // private method for UTF-8 decoding
        _utf8_decode : function (utftext) {
                var string = "";
                var i = 0;
                var c = c1 = c2 = 0;
     
                while ( i < utftext.length ) {
     
                        c = utftext.charCodeAt(i);
     
                        if (c < 128) {
                                string += String.fromCharCode(c);
                                i++;
                        }
                        else if((c > 191) && (c < 224)) {
                                c2 = utftext.charCodeAt(i+1);
                                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                                i += 2;
                        }
                        else {
                                c2 = utftext.charCodeAt(i+1);
                                c3 = utftext.charCodeAt(i+2);
                                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                                i += 3;
                        }
     
                }
     
                return string;
        }
     
    }
   
    chr = function(code)
    {
        return String.fromCharCode(code);
    };
   
    //returns utf8 encoded charachter of a unicode value.
    //code must be a number indicating the Unicode value.
    //returned value is a string between 1 and 4 charachters.
    code2utf = function(code)
    {
        if (code < 128) return chr(code);
        if (code < 2048) return chr(192+(code>>6)) + chr(128+(code&63));
        if (code < 65536) return chr(224+(code>>12)) + chr(128+((code>>6)&63)) + chr(128+(code&63));
        if (code < 2097152) return chr(240+(code>>18)) + chr(128+((code>>12)&63)) + chr(128+((code>>6)&63)) + chr(128+(code&63));
    };
   
    //it is a private function for internal use in utf8Encode function
    _utf8Encode = function(str)
    {  
        var utf8str = new Array();
        for (var i=0; i<str.length; i++) {
                utf8str[i] = code2utf(str.charCodeAt(i));
        }
        return utf8str.join('');
    };
   
    //Encodes a unicode string to UTF8 format.
    utf8Encode = function(str)
    {  
        var utf8str = new Array();
        var pos,j = 0;
        var tmpStr = '';
       
        while ((pos = str.search(/[^\x00-\x7F]/)) != -1) {
                tmpStr = str.match(/([^\x00-\x7F]+[\x00-\x7F]{0,10})+/)[0];
                utf8str[j++] = str.substr(0, pos);
                utf8str[j++] = _utf8Encode(tmpStr);
                str = str.substr(pos + tmpStr.length);
        }
       
        utf8str[j++] = str;
        return utf8str.join('');
    };
   
    a = "";
    for (var i = 0; i < 100; i++)
       a += "teststring ";
   
   
};
</script>

Test runner

Warning! For accurate results, please disable Firebug before running the tests. (Why?)

Java applet disabled.

Testing in unknown unknown
Test Ops/sec
Native btoa function
b = btoa(a);
pending…
javascript btoa
b = Base64.encode(a);
pending…
native btoa with javscript utf-8 encode
b = btoa(Base64._utf8_encode(a));
 
pending…
native btoa with native utf-8 encoding
b = btoa(unescape(encodeURIComponent(a)));
 
pending…
native btoa with javascript utf-8 #2
b = btoa(utf8Encode(a));
 
pending…
Simple escape (alternative to base64 for data URLs)
b = escape(a);
pending…

Compare results of other browsers

Revisions

You can edit these tests or add even more tests to this page by appending /edit to the URL. Here’s a list of current revisions for this page:

0 comments

Add a comment