html encode

JavaScript performance comparison

Revision 8 of this test case created by Eamon Nerbonne

Preparation code

<pre id="output_vis"></pre>
      
<script>
Benchmark.prototype.setup = function() {
  function encodeWithIfs(str) {
    var result = ""
    for (var cc = 0; cc < str.length; cc++) {
      var c = str.charAt(cc)
      if (c === "&") result += '&amp;'
      else if (c === '"') result += '&quot;'
      else if (c === "'") result += '&#39;'
      else if (c === "<") result += '&lt;'
      else if (c === ">") result += '&gt;'
      else result += c
    }
    return result
  }
  
  function endcodeWithRegEx(str) {
    return str.replace(/&/g, '&amp;').replace(/"/g, '&quot;').replace(/'/g, '&#39;').replace(/</g, '&lt;').replace(/>/g, '&gt;')
  }
  
  function endcodeWithRegEx2(str) {
    return str.replace(/[&<>"'`]/g, function (chr) {
      return '&#' + chr.charCodeAt(0) + ';'
    })
  }
  
  function encodeWithSwitch(str) {
     var c, result = "", length = str.length;
    for (var i = 0; i < length; i++) {
      switch(c = str.charAt(i)) {
        case "&": c = "&amp;"; break;
        case '"': c = "&quot;"; break;
        case "'": c = "&#39;"; break;
        case "<": c = "&lt;"; break;
        case ">": c = "&gt;"; break;
      }
      result += c;
    }
    return result;
  }
  
  var encodeWithObjectProps = function(){
    var map = {'"':'&#34;','&':'&amp;',"'":'&#39;','<':'&lt;','>':'&gt;'};
    return function (str) {
      var result = "", len = str.length, doneUpto = 0;
      for (var i=0; i < len ; i++) {
        var char = str.charAt(i);
        var ref = map[char];
        if(ref === undefined) continue; 
        result += doneUpto === i ? ref : str.substring(doneUpto, i) + ref;
        doneUpto = i + 1;
      }
      if(doneUpto !== len) result += str.substring(doneUpto, len);
      return result;
    }
  }();
  
  function encodeByCharCodeJoin(str) {
    var res = [], len = str.length, doneUpto = 0;
    for (var i=0; i < len; i++) {
      var c, code = str.charCodeAt(i);
      if(code > 62) continue;
      else if(code === 38) c = "&amp;";
      else if(code === 34) c = "&quot;";
      else if(code === 39) c = "&apos;";
      else if(code === 60) c = "&lt;"; 
      else if(code === 62) c = "&gt;";
      else continue;
      if(doneUpto !== i) res.push(str.substring(doneUpto, i));
      res.push(c);
      doneUpto = i+1;
    }
    if(doneUpto !== len) res.push(str.substring(doneUpto, len));
    return res.join("");
  }
  
  function encodeByCharCodeAppend(str) {
    var result = "", len = str.length, doneUpto = 0;
    for (var i=0; i < len ; i++) {
      var ref, code = str.charCodeAt(i);
      if(code > 62) continue; //fast-path
      else if(code === 38) ref = "&amp;";
      else if(code === 34) ref = "&#34;";
      else if(code === 39) ref = "&#39;";
      else if(code === 60) ref = "&lt;"; 
      else if(code === 62) ref = "&gt;";
      else continue;
      result += doneUpto === i  ?  ref :  str.substring(doneUpto, i) + ref;
      doneUpto = i + 1;
    }
    if(doneUpto !== len) result += str.substring(doneUpto, len);
    return result;
  }
  
  var encodeByCharCodeLookup = (function(){
    var map = {'"':'&#34;','&':'&amp;',"'":'&#39;','<':'&lt;','>':'&gt;'};
    var lookup = [];
    for(var j=0;j<63;j++) lookup[j] = map[String.fromCharCode(j)];
    return function (str) {
      var result = "", len = str.length, doneUpto = 0;
      for (var i=0; i < len ; i++) {
        var code=str.charCodeAt(i);
        if(code>62) continue;
        var ref = lookup[code];
        if(ref === undefined) continue;
        result += doneUpto === i ? ref : str.substring(doneUpto, i) + ref;
        doneUpto = i + 1;
      }
      if(doneUpto !== len) result += str.substring(doneUpto, len);
      return result;
    };
  })();
  
  
  var encodeViaDom = (function(){ //limited: doesn't encode ' and "
      var el = document.createElement('div');
      return function(val) {
          el.textContent = val;
          return el.innerHTML;
      };
  })();
  
  var encodeWithRegExpAndObject = (function() {
    var o={"&":"&amp;", '"':"&quot;", "'":"&#39;", "<":"&lt;", ">":"&gt;"};
    function mapchar(chr) { return o[chr]; }
    return function (str) { return str.replace(/[&"'<>]/g, mapchar); };
  })();
  
  //buggy, doesn't work in IE10, not in chrome 23, and not in FF20
  function encodeWithStringPrototype(str) {
    return ''.link(str).slice(9,-6).replace(/&/g, '&amp;');
  }
  
  function doEncode(func) {
    //ensure JIT doesn't omit call entirely by storing output in an observable location.  Also, use this to debug...
    window.output_sink = [
      func("<div></div>"),
      func("This is a string with no replacements"),
      func("All replacement chars & \" ' < >")
    ];
  }

};

Benchmark.prototype.teardown = function() {
  document.getElementById('output_vis').textContent = window.output_sink.join("\n");

};
</script>

Preparation code output

<pre id="output_vis"></pre>

Test runner

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

Java applet disabled.

Testing in CCBot 2.0.0 / Other 0.0.0
Test Ops/sec
loop + if-charAt
doEncode(encodeWithIfs);
pending…
multiple regexes
doEncode(endcodeWithRegEx);
pending…
regex + match func
doEncode(endcodeWithRegEx2);
pending…
loop + object lookup
doEncode(encodeWithObjectProps);
pending…
regex + object lookup
doEncode(encodeWithRegExpAndObject);
pending…
using DOM innerHtml
doEncode(encodeViaDom);
pending…
loop + if-charcode; join
doEncode(encodeByCharCodeJoin);
pending…
loop + if-charcode; append
doEncode(encodeByCharCodeAppend);
pending…
loop + charcode-lookup; append
doEncode(encodeByCharCodeLookup);
pending…
loop + switch-charAt
doEncode(encodeWithSwitch);
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.

0 Comments