[JSC] Speed up URL encode/decode by using bitmaps instead of strchr().
authorakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 May 2015 17:46:43 +0000 (17:46 +0000)
committerakling@apple.com <akling@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 18 May 2015 17:46:43 +0000 (17:46 +0000)
<https://webkit.org/b/145115>

Reviewed by Anders Carlsson.

We were calling strchr() for every character when doing URL encoding/decoding and it stood out
like a sore O(n) thumb in Instruments. Optimize this by using a Bitmap<256> instead.

5.5% progression on Kraken/stanford-crypto-sha256-iterative.

* runtime/JSGlobalObjectFunctions.cpp:
(JSC::makeCharacterBitmap):
(JSC::encode):
(JSC::decode):
(JSC::globalFuncDecodeURI):
(JSC::globalFuncDecodeURIComponent):
(JSC::globalFuncEncodeURI):
(JSC::globalFuncEncodeURIComponent):
(JSC::globalFuncEscape):

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@184501 268f45cc-cd09-0410-ab3c-d52691b4dbfc

Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/runtime/JSGlobalObjectFunctions.cpp

index 6760a23..b2f73c5 100644 (file)
@@ -1,3 +1,25 @@
+2015-05-18  Andreas Kling  <akling@apple.com>
+
+        [JSC] Speed up URL encode/decode by using bitmaps instead of strchr().
+        <https://webkit.org/b/145115>
+
+        Reviewed by Anders Carlsson.
+
+        We were calling strchr() for every character when doing URL encoding/decoding and it stood out
+        like a sore O(n) thumb in Instruments. Optimize this by using a Bitmap<256> instead.
+
+        5.5% progression on Kraken/stanford-crypto-sha256-iterative.
+
+        * runtime/JSGlobalObjectFunctions.cpp:
+        (JSC::makeCharacterBitmap):
+        (JSC::encode):
+        (JSC::decode):
+        (JSC::globalFuncDecodeURI):
+        (JSC::globalFuncDecodeURIComponent):
+        (JSC::globalFuncEncodeURI):
+        (JSC::globalFuncEncodeURIComponent):
+        (JSC::globalFuncEscape):
+
 2015-05-17  Benjamin Poulain  <benjamin@webkit.org>
 
         Do not use fastMallocGoodSize anywhere
index 99d84d4..23239a8 100644 (file)
@@ -53,7 +53,16 @@ using namespace Unicode;
 
 namespace JSC {
 
-static JSValue encode(ExecState* exec, const char* doNotEscape)
+template<unsigned charactersCount>
+static Bitmap<256> makeCharacterBitmap(const char (&characters)[charactersCount])
+{
+    Bitmap<256> bitmap;
+    for (unsigned i = 0; i < charactersCount; ++i)
+        bitmap.set(characters[i]);
+    return bitmap;
+}
+
+static JSValue encode(ExecState* exec, const Bitmap<256>& doNotEscape)
 {
     CString cstr = exec->argument(0).toString(exec)->value(exec).utf8(StrictConversion);
     if (!cstr.data())
@@ -63,7 +72,7 @@ static JSValue encode(ExecState* exec, const char* doNotEscape)
     const char* p = cstr.data();
     for (size_t k = 0; k < cstr.length(); k++, p++) {
         char c = *p;
-        if (c && strchr(doNotEscape, c))
+        if (c && doNotEscape.get(static_cast<LChar>(c)))
             builder.append(static_cast<LChar>(c));
         else {
             builder.append(static_cast<LChar>('%'));
@@ -75,7 +84,7 @@ static JSValue encode(ExecState* exec, const char* doNotEscape)
 
 template <typename CharType>
 ALWAYS_INLINE
-static JSValue decode(ExecState* exec, const CharType* characters, int length, const char* doNotUnescape, bool strict)
+static JSValue decode(ExecState* exec, const CharType* characters, int length, const Bitmap<256>& doNotUnescape, bool strict)
 {
     JSStringBuilder builder;
     int k = 0;
@@ -127,7 +136,7 @@ static JSValue decode(ExecState* exec, const CharType* characters, int length, c
                     u = Lexer<UChar>::convertUnicode(p[2], p[3], p[4], p[5]);
                 }
             }
-            if (charLen && (u == 0 || u >= 128 || !strchr(doNotUnescape, u))) {
+            if (charLen && (u == 0 || u >= 128 || !doNotUnescape.get(static_cast<LChar>(u)))) {
                 builder.append(u);
                 k += charLen;
                 continue;
@@ -139,7 +148,7 @@ static JSValue decode(ExecState* exec, const CharType* characters, int length, c
     return builder.build(exec);
 }
 
-static JSValue decode(ExecState* exec, const char* doNotUnescape, bool strict)
+static JSValue decode(ExecState* exec, const Bitmap<256>& doNotUnescape, bool strict)
 {
     String str = exec->argument(0).toString(exec)->value(exec);
     
@@ -626,46 +635,51 @@ EncodedJSValue JSC_HOST_CALL globalFuncIsFinite(ExecState* exec)
 
 EncodedJSValue JSC_HOST_CALL globalFuncDecodeURI(ExecState* exec)
 {
-    static const char do_not_unescape_when_decoding_URI[] =
-        "#$&+,/:;=?@";
+    static Bitmap<256> doNotUnescapeWhenDecodingURI = makeCharacterBitmap(
+        "#$&+,/:;=?@"
+    );
 
-    return JSValue::encode(decode(exec, do_not_unescape_when_decoding_URI, true));
+    return JSValue::encode(decode(exec, doNotUnescapeWhenDecodingURI, true));
 }
 
 EncodedJSValue JSC_HOST_CALL globalFuncDecodeURIComponent(ExecState* exec)
 {
-    return JSValue::encode(decode(exec, "", true));
+    static Bitmap<256> emptyBitmap;
+    return JSValue::encode(decode(exec, emptyBitmap, true));
 }
 
 EncodedJSValue JSC_HOST_CALL globalFuncEncodeURI(ExecState* exec)
 {
-    static const char do_not_escape_when_encoding_URI[] =
+    static Bitmap<256> doNotEscapeWhenEncodingURI = makeCharacterBitmap(
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
         "abcdefghijklmnopqrstuvwxyz"
         "0123456789"
-        "!#$&'()*+,-./:;=?@_~";
+        "!#$&'()*+,-./:;=?@_~"
+    );
 
-    return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI));
+    return JSValue::encode(encode(exec, doNotEscapeWhenEncodingURI));
 }
 
 EncodedJSValue JSC_HOST_CALL globalFuncEncodeURIComponent(ExecState* exec)
 {
-    static const char do_not_escape_when_encoding_URI_component[] =
+    static Bitmap<256> doNotEscapeWhenEncodingURIComponent = makeCharacterBitmap(
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
         "abcdefghijklmnopqrstuvwxyz"
         "0123456789"
-        "!'()*-._~";
+        "!'()*-._~"
+    );
 
-    return JSValue::encode(encode(exec, do_not_escape_when_encoding_URI_component));
+    return JSValue::encode(encode(exec, doNotEscapeWhenEncodingURIComponent));
 }
 
 EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec)
 {
-    static const char do_not_escape[] =
+    static Bitmap<256> doNotEscape = makeCharacterBitmap(
         "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
         "abcdefghijklmnopqrstuvwxyz"
         "0123456789"
-        "*+-./@_";
+        "*+-./@_"
+    );
 
     JSStringBuilder builder;
     String str = exec->argument(0).toString(exec)->value(exec);
@@ -673,7 +687,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec)
         const LChar* c = str.characters8();
         for (unsigned k = 0; k < str.length(); k++, c++) {
             int u = c[0];
-            if (u && strchr(do_not_escape, static_cast<char>(u)))
+            if (u && doNotEscape.get(static_cast<LChar>(u)))
                 builder.append(*c);
             else {
                 builder.append(static_cast<LChar>('%'));
@@ -692,7 +706,7 @@ EncodedJSValue JSC_HOST_CALL globalFuncEscape(ExecState* exec)
             builder.append(static_cast<LChar>('u'));
             appendByteAsHex(u >> 8, builder);
             appendByteAsHex(u & 0xFF, builder);
-        } else if (u != 0 && strchr(do_not_escape, static_cast<char>(u)))
+        } else if (u != 0 && doNotEscape.get(static_cast<LChar>(u)))
             builder.append(*c);
         else {
             builder.append(static_cast<LChar>('%'));