file reader vs base64 vs derect

Benchmark created on


Description

benchmark small blob (16k) to binary string in 2 different way: async: file reader sync: arraybuffer -> string (base64/direct)

Setup

var size = 16 * 1024 ;
    var ab = new ArrayBuffer(size);
    var teststr = "";
    for (var i = 0; i < size; i++) {
      teststr += "A";
    };
    var bv = new Uint8Array(ab);
    for (var i = 0, strLen = teststr.length; i < strLen; i++) {
      bv[i] = teststr.charCodeAt(i);
    }
    var bl = new Blob([ab]);
    
    var encodings = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-/=';
    
    var encode = function (arrayBuffer) {
                var base64 = '';
    
                var bytes = new Uint8Array(arrayBuffer);
                var byteLength = bytes.byteLength;
                var byteRemainder = byteLength % 3;
                var mainLength = byteLength - byteRemainder;
    
                var a, b, c, d;
                var chunk;
    
                // Main loop deals with bytes in chunks of 3
                for (var i = 0; i < mainLength; i = i + 3) {
                    // Combine the three bytes into a single integer
                    chunk = (bytes[i] << 16) | (bytes[i + 1] << 8) | bytes[i + 2];
    
                    // Use bitmasks to extract 6-bit segments from the triplet
                    a = (chunk & 16515072) >> 18; // 16515072 = (2^6 - 1) << 18
                    b = (chunk & 258048) >> 12; // 258048   = (2^6 - 1) << 12
                    c = (chunk & 4032) >> 6; // 4032     = (2^6 - 1) << 6
                    d = chunk & 63;               // 63       = 2^6 - 1
    
                    // Convert the raw binary segments to the appropriate ASCII encoding
                    base64 += encodings[a] + encodings[b] + encodings[c] + encodings[d]
                }
    
                // Deal with the remaining bytes and padding
                if (byteRemainder == 1) {
                    chunk = bytes[mainLength];
    
                    a = (chunk & 252) >> 2; // 252 = (2^6 - 1) << 2
    
                    // Set the 4 least significant bits to zero
                    b = (chunk & 3) << 4; // 3   = 2^2 - 1
    
                    base64 += encodings[a] + encodings[b] + '==';
                } else if (byteRemainder == 2) {
                    chunk = (bytes[mainLength] << 8) | bytes[mainLength + 1];
    
                    a = (chunk & 64512) >> 10; // 64512 = (2^6 - 1) << 10
                    b = (chunk & 1008) >> 4; // 1008  = (2^6 - 1) << 4
    
                    // Set the 2 least significant bits to zero
                    c = (chunk & 15) << 2; // 15    = 2^4 - 1
    
                    base64 += encodings[a] + encodings[b] + encodings[c] + '='
                }
    
                return base64
            };
    
    
    
    var lookup = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
    
    
    
      var Arr = Uint8Array;
    
        var PLUS   = '+'.charCodeAt(0)
        var SLASH  = '/'.charCodeAt(0)
        var NUMBER = '0'.charCodeAt(0)
        var LOWER  = 'a'.charCodeAt(0)
        var UPPER  = 'A'.charCodeAt(0)
    
        function decode (elt) {
                var code = elt.charCodeAt(0)
                if (code === PLUS)
                        return 62 // '+'
                if (code === SLASH)
                        return 63 // '/'
                if (code < NUMBER)
                        return -1 //no match
                if (code < NUMBER + 10)
                        return code - NUMBER + 26 + 26
                if (code < UPPER + 26)
                        return code - UPPER
                if (code < LOWER + 26)
                        return code - LOWER + 26
        }
    
        function b64ToByteArray (b64) {
                var i, j, l, tmp, placeHolders, arr
    
                if (b64.length % 4 > 0) {
                        throw new Error('Invalid string. Length must be a multiple of 4')
                }
    
                // the number of equal signs (place holders)
                // if there are two placeholders, than the two characters before it
                // represent one byte
                // if there is only one, then the three characters before it represent 2 bytes
                // this is just a cheap hack to not do indexOf twice
                var len = b64.length
                placeHolders = '=' === b64.charAt(len - 2) ? 2 : '=' === b64.charAt(len - 1) ? 1 : 0
    
                // base64 is 4/3 + up to two characters of the original data
                arr = new Arr(b64.length * 3 / 4 - placeHolders)
    
                // if there are placeholders, only get up to the last complete 4 chars
                l = placeHolders > 0 ? b64.length - 4 : b64.length
    
                var L = 0
    
                function push (v) {
                        arr[L++] = v
                }
    
                for (i = 0, j = 0; i < l; i += 4, j += 3) {
                        tmp = (decode(b64.charAt(i)) << 18) | (decode(b64.charAt(i + 1)) << 12) | (decode(b64.charAt(i + 2)) << 6) | decode(b64.charAt(i + 3))
                        push((tmp & 0xFF0000) >> 16)
                        push((tmp & 0xFF00) >> 8)
                        push(tmp & 0xFF)
                }
    
                if (placeHolders === 2) {
                        tmp = (decode(b64.charAt(i)) << 2) | (decode(b64.charAt(i + 1)) >> 4)
                        push(tmp & 0xFF)
                } else if (placeHolders === 1) {
                        tmp = (decode(b64.charAt(i)) << 10) | (decode(b64.charAt(i + 1)) << 4) | (decode(b64.charAt(i + 2)) >> 2)
                        push((tmp >> 8) & 0xFF)
                        push(tmp & 0xFF)
                }
    
                return arr
        }
    
        function uint8ToBase64 (uint8) {
                var i,
                        extraBytes = uint8.length % 3, // if we have 1 byte left, pad 2 bytes
                        output = "",
                        temp, length
    
                function encode (num) {
                        return lookup.charAt(num)
                }
    
                function tripletToBase64 (num) {
                        return encode(num >> 18 & 0x3F) + encode(num >> 12 & 0x3F) + encode(num >> 6 & 0x3F) + encode(num & 0x3F)
                }
    
                // go through the array every three bytes, we'll deal with trailing stuff later
                for (i = 0, length = uint8.length - extraBytes; i < length; i += 3) {
                        temp = (uint8[i] << 16) + (uint8[i + 1] << 8) + (uint8[i + 2])
                        output += tripletToBase64(temp)
                }
    
                // pad the end with zeros, but make sure to not forget the extra bytes
                switch (extraBytes) {
                        case 1:
                                temp = uint8[uint8.length - 1]
                                output += encode(temp >> 2)
                                output += encode((temp << 4) & 0x3F)
                                output += '=='
                                break
                        case 2:
                                temp = (uint8[uint8.length - 2] << 8) + (uint8[uint8.length - 1])
                                output += encode(temp >> 10)
                                output += encode((temp >> 4) & 0x3F)
                                output += encode((temp << 2) & 0x3F)
                                output += '='
                                break
                }
    
                return output
        };

Test runner

Ready to run.

Testing in
TestOps/sec
file reader
// async test
var fr = new FileReader();
fr.onload = function(evt) {deferred.resolve();};
fr.readAsBinaryString(bl);
ready
direct
var str = "";
var buf = new Uint8Array(ab);
for (var i = 0; i < buf.byteLength; i++) {
  str += String.fromCharCode(buf[i]);
}
ready
base64
encode(ab);
ready
base64 2
uint8ToBase64(bv);
ready
direct 2
String.fromCharCode.apply(null, new Uint16Array(ab));
ready

Revisions

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