Native base64 vs pure-JS

Benchmark created by ChipX86 on


Description

Checking how fast a JS-based base64 implementation is vs. using window.btoa and window.atob.

Preparation HTML

<script>
  var Base64 = {

    /* Convert data (an array of integers) to a Base64 string. */
    toBase64Table: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/',
    base64Pad: '=',

    encode: function(data) {
      "use strict";
/*
        if (window.btoa) {
          return window.btoa(data);
        }
        */

      var result = '',
          chrTable = Base64.toBase64Table.split(''),
          pad = Base64.base64Pad,
          length = data.length,
          iterLength = length - 2,
          lengthMod3 = length % 3,
          i;
      // Convert every three bytes to 4 ascii characters.
      for (i = 0; i < iterLength; i += 3) {
        result += chrTable[data[i] >> 2];
        result += chrTable[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
        result += chrTable[((data[i + 1] & 0x0f) << 2) + (data[i + 2] >> 6)];
        result += chrTable[data[i + 2] & 0x3f];
      }

      // Convert the remaining 1 or 2 bytes, pad out to 4 characters.
      if (lengthMod3) {
        i = length - lengthMod3;
        result += chrTable[data[i] >> 2];
        if (lengthMod3 === 2) {
          result += chrTable[((data[i] & 0x03) << 4) + (data[i + 1] >> 4)];
          result += chrTable[(data[i + 1] & 0x0f) << 2];
          result += pad;
        } else {
          result += chrTable[(data[i] & 0x03) << 4];
          result += pad + pad;
        }
      }

      return result;
    },

    /* Convert Base64 data to a string */
    toBinaryTable: [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 0, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1],

    decode: function(data, offset) {
      "use strict";
/*
        if (window.atob) {
          return window.atob(data.substr(offset));
        }
        */

      offset = typeof(offset) !== 'undefined' ? offset : 0;
      var binTable = Base64.toBinaryTable,
          pad = Base64.base64Pad,
          result, result_length, idx, i, c, padding, leftbits = 0,
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          // number of bits decoded, but yet to be appended
          leftdata = 0,
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          
          // bits decoded, but yet to be appended
          data_length = data.indexOf('=') - offset,
          length = data.length;

      if (data_length < 0) {
        data_length = data.length - offset;
      }

      /* Every four characters is 3 resulting numbers */
      result_length = (data_length >> 2) * 3 + Math.floor((data_length % 4) / 1.5);
      result = new Array(result_length);

      // Convert one by one.
      for (idx = 0, i = offset; i < length; i++) {
        c = binTable[data.charCodeAt(i) & 0x7f];
        padding = (data.charAt(i) === pad);
        // Skip illegal characters and whitespace
        if (c === -1) {
          console.error("Illegal character '" + data.charCodeAt(i) + "'");
          continue;
        }

        // Collect data into leftdata, update bitcount
        leftdata = (leftdata << 6) | c;
        leftbits += 6;

        // If we have 8 or more bits, append 8 bits to the result
        if (leftbits >= 8) {
          leftbits -= 8;
          // Append if not padding.
          if (!padding) {
            result[idx++] = (leftdata >> leftbits) & 0xff;
          }
          leftdata &= (1 << leftbits) - 1;
        }
      }

      // If there are any bits left, the base64 string was corrupted
      if (leftbits) {
        throw {
          name: 'Base64-Error',
          message: 'Corrupted base64 string'
        };
      }

/*
        var result2 = window.atob(data.substr(offset)).split('');
        if (result != result2) {
           console.log("*** Decode fail:");
           console.log(result);
           console.log(result2);
        }
        */

      return result;
    }

  };
</script>

Setup

var b64encoded = 'iVBORw0KGgoAAAANSUhEUgAAADMAAAAyCAIAAAB+n3TYAAAIL2lDQ1BJQ0MgcHJvZmlsZQAAeNrtmnlUlPUaxz/vOxvDOuIom8gAKSKBM6yS3VTALRENRkVzAVnEhUQYhSHNSlFw6ea+5JYZqakhablv5MpVTAIyssIlNYlcyPRKev8YEK7p9Xju/eOew3zPec/7zO9d5n0+v+c85/ee7wvtXgQQu0HqG4b06F5hmtghQzWKSqS0RIWULvEJGWn9YnrqAaL6R/XgL7pTjgBQ6hs+OSaN55NVYlJGAggWQHpCWroBhClAYKYhzQBCAaBOjx0yFISvAPVoU/wtoB5liqsBdbo+OhxEANvRplgF2I4yxRrAdnLCaAOIWpDY68MHGEAWAW5XTc8AgKM+Zcwb4wbEJ2oGB/lrNbpQf22WTttZy/9ahqQsA0CHBG9/rTZUExFviE+YMH5CuqZDTJoxMSk9oMf4MYYkTYCfzi/IV+cNEDtkqMZ0da0MARBaFzWOrb0Eke+AuKVxLGAcrI+DdhsbxzxmgZ0bbCpPmJQ+uf5xBDEAnvXbxK9eEgEEEARRFCUSqVQqk8nlcoXCQqm0tLKytrGxtVOpWti3VKtbtXZwdHJ2cWnj2tZN4+7h+UK79l4dvDv6vOjr10mr8w8IDA7pHPpSl5f/9krXbmHhET169urd59XIflH9B7wWHTNw0ODYIUNfHzZiZFz8qITEpNEpY8aOG5/6RtrE9AzDpMmZWdlvTpn61rS333l3es7MWbl5s+fMnffe39+fv3DR4iVLly1f8cHKVavXrP1w3UfrP87/ZOOmTzdv2fpZwbbCz7fv+OLLnbt279m7b/+Bg4cOF3115Oix4ydOFv/j1OmSM1+fLf2mrLzi23PfVX5//ocff6q6cPHS5cs/X7l67Zfr1b/W/Hbj5q3btb/f+eOeKX+JxJS9QqGwsFBaWlpZW9vY/lv6jk7OLm1c2z4l/6AmALrXE3i1bwMB/UATgeEjRsbFJyQmJdcjmPAIgfERghk5MxsRLFi4aPGSxxFs2PhXBI8RaATQkP+Fi5cu/3zl6i/Xq3+tuXHz1u3aO3/cvXe/7s+HINQXQEMFKBQWFkql0tLSysra2qYeQwt7+5ZqdatWrR0cHJ1MpeDa1k2jcffw9HyhXfv2Xh28O5p4NABpQuQRkj4NTF6LjtE31MXwhsJIHp0yxlQaTcAYs5uimZX7GJynFci2ws+3f/HlThOaw0VHjh4/WXzq9Jmz35RVnKs8/2PVxctXrl2vuXHrdzD1EQBkgNwUGpdTNnIKgAAE++n+Yy8QgCO/+T6zZ9g1OV+s3wuPnSPWb4/fHyAjU6Pz8++UkqkJDI98wv21Oq1/aIgu5Gn/r9NpA3WhuqCgAJ3umXFnnbYF0HRbMh3KsuDUZpgzXmD3Ndi7HVImSrC+IHDjGEQ6yEn1klBcBbapSvIOSTmCQOwuG26dlZPcXsBhlz1lnZXcihY4NseRr0VrKqYLjHB2o87bjq47BLb28sLXviXqGoHCfVpKljoQGijSad9L1A5vQ9dJIlXZPVHddSd6p8i6nBh2e3egpk5ElTWKvO6dMPaREHIigxHZISzMklAQPRtX5zCmbZJgllnPq6b1ry+CzcMEqm9C5giBB4sk+HwiYOsrMn+enJKlIuu1ElZGWJJ3SMKS6VIWOtqR01fGNEs5Lp+psZcocClRUOHpQk2dBWKxku29PZAGWxHWzZqvPXyIumBDvosdPQcFEVqrYr+bPWPbdCPWSU3Uu63oLUSSketA1UtOnKqOpcDfBXl3Vyp3JWN/sS21592Z1HIy8yo82LzXC31RLkfwMk+mWf9V/UvLYcVymDpN5NAmgYjrEBWmoN1RkcjBAoWrbFjhJGXs9wKpvVoRUCrDmCdS4O9GlwMKSvtJWD+gIyk3lZzzk1J5LxinCGvk7jKuJUewuc4W+3A5Rm89FRJ7bOcpkH6RxN3bakoqLFhdMpko0RHvly0pXJXLVDcXXHdbce70CrqWueLWzoY7xzcR5aGhdJ4tc+buZNBpd/Lv27Hh6GHzZJr13Fo5AyqNsCEftipETtwRGPu9iP4jGZmrZUStsyDUYElGriVhcSoclquYVa5C9HGGOEc8Kx1RR7WnKtudf77iQWRIIJNu+7DW2RfLinDyPgyhMjQU2y16Mvf3IOpCL1b3T2GF0yA8K2Nxkr5FXlUylUPGklO8gNJTRpZI3qJoxwaqsuexMnEBD9T7UO1cxwPDBrb3PoPn/V1c6XKQE+U/4XDuLGLfczjcu26eTLOeW/XvBsHawKDQZ8RBQZ2DgoUm7zhKoEvX2agcwyhbZl5/m9X85DXfizrvXDIszOtvs5qfDp10p+yuHfvXmNffZjU/nZ8BK4yQnA+FriIPAkXickTk7kpyipXMGW/JlT2tqdY5Ivo4884OH84n+rH55QBKHHpTU9cXr/kxhKrHke8ykZTXsunaajH68FVYX9iEfUIxU0eWER171QzbrP87BQSIvHIbQCCttUCiE0gAByMkupQ/8RqhiUSTUyWRSKVSqUwmk8nl8gbXzmRZ1XtWtnZ2qkbbqnWjbWXy8DTu9S6eycar9/GeYOQ1Na1i9AMHxQ55fdjwEXHxoxKSklPGjB1nsjEnZWYZ35wy7e13p+fMzM2bM/e99+cvWLR46bIPVq5es+6jj/M3frpla8G2z3fs3LVn74GDh4uOHj9ZfLrkbGl5xXeVP/x48dKVa9erf7txu/aPu/f/fGjOv3nn3+D1PckbfKS4uG5PO+Tv5+8bHKTVNtce0+Cd9opPTY3X+DfbXvvoO5iIMRlp4+ONjZ/C+OqaD4WHj/rJvwAhONjPHZVUEwAAAAlwSFlzAAAV/gAAFiUBAEPSgQAAAAd0SU1FB9sJAwU5FLkdEP8AAAAZdEVYdENvbW1lbnQAQ3JlYXRlZCB3aXRoIEdJTVBXgQ4XAAAGhklEQVRYw+2Ye0xTdxTHz723ULx9SCmFltZCC1gVZIaoCCIOXwMUnROdjy0uWbb9sbjskblly+KWmE3dGDNbNh+LbnEuWcDMbZotIaA4Xs4HZQLy0La4tkChhVvb2973/sAtDAGBInEJ5997883ndx6/3zkHEQQBHklD4VG1GbIZsukw0Xh+ctoqG2rearp5TSUfuZAFAXh+9NOjSKx26ZLHS2J1meMnQx54a3AcVXp0TubK9e8edG0q0G3M145AxnVyzLV7/7NulrVzLDA0cCywLNA0dDugu2fO1pcsKCqaymgKAH6S7fzLd+SE5UbzwPREExnPTeu0VR77fLXFvWNr0c7iTz/KWljLcsAwcooRG7W9CikIAowhgyCg1mVMNJoP9q2PuNPYcKbJmrpyddbyrGVdXc9euz4fxyW3HZ2KSOHl19NRFOGZBp6zAgg09SdDAUUBTQHHAQBQFFytQzT6NRKZdsp85vH0fVG87eqN4F1SnLNixTM7dymVysFPDofz1TdeO1KyRK/DAYBnGnnu9ohkARJumEGhDO/tQXTGJ3I3lWFYWEg+43m+8ucd7Vav1y/b996+x9JSh1SicKj4k907DINYY+cry4BIBElzaUMitLaUV57dsnrzWRRFJ18BreajGFf7VTH71h7qowP7DhUfHJI3CMMEk4zSob4fvbSBphD7HRGGwfyUQH9vRav5aEi16bSdT5pLijBUhplfLPJWVFQzDAMAt25beJ4vyCs880vXEDB0DDJ51EKfP81mmYWiEKsmHdbzIZH12OtUMfEAYL+D5RZ+GxERThDE/g8/fPudt4uefrq65lJ1rd3toR/oM5YFucKYnX+yrxcDALkceux1IeUZQ5Nh4mgACJI0LlWLxeFv7N2bliI5e3qF9y5Td6VPJom7VOvavEEHAADYaDokiUVr03CpmqZoABCJgKHJkMgUquQBtw3HkxRKfMDdJpHgi+b2vLO3AABUYmxjvm5jvm48eUb6ZdHqxQPuNqkMB6D9PlAok0OKZnRshsdzFwBUsaS19fSB/e+b4homoeMlKKU63XLztHw2CQA+H0RrMkIiU+vXdDtkAGBKSehoOmU0JGCiyIA/MMotP1yHYeCncqAoQFBRWBje0XQqTksDADEg08SvCYnMMK8oEIzsdtpwXGJMFKrObY+KSSf6u8aWo/8piS9Pzf/14jIvAVGqhRd+2a5WC+FiIPqB5SIN84pCIkNRbMnjJQ1/MACQnmGiyWqi3+rpc46tU3buscLdmbv2ZDtcyTgur6oHn7fLT1QnJAYAwGqRLs0tQVEs1F7DMG8LJk68Wt+CokjO2kSFor3H2TK2zvYnG5MM6Pr89YdLPn/phT1V19d9VRYTBBJBwNohFkckGuZtmZouKH97Vf/AgtpLNzEMXbxsvtvlYFl2DB0UhczFrj63i+N4r5dgaf9zRZcXp0FHa0SQWVCwq2oquyCWCZSfyfMT1+fE+929QnLqVr0x9b7O0ckx9YMv+u5Xlrs9CIqCRqPVKn4qyKHcbok8Kn1d0W+isFlT2QWJwmY9sa2iq7Pqzq2yfs8PTddL7ycD5N591ueO2bDWkZ5iI0nYfzhDrwjHo55bkFGkiV85/m52Ap3jUCs9lpCZs1KlSfyPz3gXR1cP7YJc3WBuVGYX1pjmmqZpdlq+7kTthTKWDQ474bBXvLVZsmrD8UljTYYsLmGV1riroa4UQBhNp60lQq3PM5g2T/e8uTS3xOOW/l5+iGWIYT5jGbj2B+7z67Pzjk/HhDJS18XUl79st3xvSomLjk2SyaWe3nK3a6CjTaKJL8zOOzGhMpxKskH7s/7jNvMnd72uwUkYQUARbcrOOzmhGWnqyTiOKjumy8rdplLHIAiwwR9pqr3ZHHA4Jjbx/v/2GpP0mddz21y3r/uvi1TAyTDCv9HEMGSWRBMTt2xR1gdRManTShYke69cfNNu+yEtPS9GkxyBi8PDwwCAoyoEgfARjf0e6OlGbJYInSEvY9Vn0tn66SDjee78dxna+ChTaqZI9J9mhqMqBWGAoRqpIFAUkD7otGKEV//U882Tq9OJ5Zm55oPZSkhZlD0M634pTASGJC48rPtyxavTUQG3Wr5JScsZda9ynyUYA9b2soe42ft3zvP222sqvx7xa4AUeA4EQTq46LuXI4IQCHiCZG8ErnqIecbzrN9rn8TpcZl2nFuWKXsDZnbbM2QzZDNkM2TjsL8BnS8HYhxB7B4AAAAASUVORK5CYII=';
    var b64decoded = window.atob(b64encoded);

Test runner

Ready to run.

Testing in
TestOps/sec
Base64.decode
Base64.decode(b64encoded);
ready
Base64.encode
Base64.encode(b64decoded);
ready
window.btoa
window.btoa(b64decoded);
ready
window.atob
window.atob(b64encoded);
ready
window.btoa with source charcode array
var s = '',
    length = b64decoded.length,
    i;

for (i = 0; i < length; i++) {
  s += String.fromCharCode(b64decoded[i]);
}

window.btoa(s);
ready
window.atob with resulting charcode array (using map)
window.atob(b64encoded).split('').map(function(x) {
  return x.charCodeAt(0);
});
ready
window.atob with resulting charcode array (using for loop)
var bytes = window.atob(b64encoded).split(''),
    len = bytes.length,
    result = [],
    i;

for (i = 0; i < length; i++) {
  result.push(bytes.charCodeAt(i));
}
ready
window.atob with resulting charcode array (using for loop and batches of 8)
var bytes = window.atob(b64encoded),
    len = bytes.length,
    result = new Array(length),
    i;

for (i = 0; i < length; i += 8) {
  result[i] = bytes.charCodeAt(i);
  result[i + 1] = bytes.charCodeAt(i + 1);
  result[i + 2] = bytes.charCodeAt(i + 2);
  result[i + 3] = bytes.charCodeAt(i + 3);
  result[i + 4] = bytes.charCodeAt(i + 4);
  result[i + 5] = bytes.charCodeAt(i + 5);
  result[i + 6] = bytes.charCodeAt(i + 6);
  result[i + 7] = bytes.charCodeAt(i + 7);
}

for (i; i < length; i++) {
  result[i] = bytes.charCodeAt(i);
}
ready
window.btoa with source charcode array (batches of 8)
var s = '',
    length = b64decoded.length,
    i;

for (i = 0; i + 8 <= length; i += 8) {
  s += String.fromCharCode(b64decoded[i], b64decoded[i + 1], b64decoded[i + 2], b64decoded[i + 3], b64decoded[i + 4], b64decoded[i + 5], b64decoded[i + 6], b64decoded[i + 7]);
}

for (i = s.length; i < length; i++) {
  s += String.fromCharCode(b64decoded[i]);
}

window.btoa(s);
ready
window.btoa with source charcode array (batches of 8 with array)
var length = b64decoded.length,
    array = new Array(length),
    i;

for (i = 0; i + 8 <= length; i += 8) {
  array[i] = String.fromCharCode(b64decoded[i]);
  array[i + 1] = String.fromCharCode(b64decoded[i + 1]);
  array[i + 2] = String.fromCharCode(b64decoded[i + 2]);
  array[i + 3] = String.fromCharCode(b64decoded[i + 3]);
  array[i + 4] = String.fromCharCode(b64decoded[i + 4]);
  array[i + 5] = String.fromCharCode(b64decoded[i + 5]);
  array[i + 6] = String.fromCharCode(b64decoded[i + 6]);
  array[i + 7] = String.fromCharCode(b64decoded[i + 7]);
}

for (; i < length; i++) {
  array[i] = String.fromCharCode(b64decoded[i]);
}

window.btoa(array.join(''));
ready
window.btoa with source charcode array (batches of 8 with array and combined fromCharCode)
var length = b64decoded.length,
    array = new Array(Math.ceil(length / 8)),
    i, j;

for (i = 0, j = 0; i < length; i += 8, j++) {
  array[j] = String.fromCharCode(b64decoded[i], b64decoded[i + 1], b64decoded[i + 2], b64decoded[i + 3], b64decoded[i + 4], b64decoded[i + 5], b64decoded[i + 6], b64decoded[i + 7]);
}

window.btoa(array.join('').substr(0, length));
ready

Revisions

You can edit these tests or add more tests to this page by appending /edit to the URL.

  • Revision 1: published by ChipX86 on
  • Revision 2: published by Andrew Kimpton on