Bug 1490973 - Avoid repeatedly calling Append() in NS_UnescapeURL(). r=froydnj
authorHenri Sivonen <hsivonen@hsivonen.fi>
Thu, 13 Sep 2018 20:49:31 +0000
changeset 494752 4760a22b8b89893238fbda27e5b4d9cf3dd014db
parent 494751 17bd2cc6216a2305175425da0c3421f01fa4ae9d
child 494753 2e5441b820524a231d6a52284cca2db3c8e0ff19
push id1864
push userffxbld-merge
push dateMon, 03 Dec 2018 15:51:40 +0000
treeherdermozilla-release@f040763d99ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersfroydnj
bugs1490973
milestone64.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1490973 - Avoid repeatedly calling Append() in NS_UnescapeURL(). r=froydnj MozReview-Commit-ID: H29SAvXS4Jl Differential Revision: https://phabricator.services.mozilla.com/D5769
xpcom/io/nsEscape.cpp
--- a/xpcom/io/nsEscape.cpp
+++ b/xpcom/io/nsEscape.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsEscape.h"
 
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/BinarySearch.h"
 #include "mozilla/CheckedInt.h"
+#include "mozilla/TextUtils.h"
 #include "nsTArray.h"
 #include "nsCRT.h"
 #include "plstr.h"
 #include "nsASCIIMask.h"
 
 static const char hexCharsUpper[] = "0123456789ABCDEF";
 static const char hexCharsUpperLower[] = "0123456789ABCDEFabcdef";
 
@@ -516,18 +517,16 @@ NS_EscapeURL(const nsString& aStr, const
     }
   }
   if (MOZ_UNLIKELY(didEscape)) {
     return aResult;
   }
   return aStr;
 }
 
-#define ISHEX(c) memchr(hexCharsUpperLower, c, sizeof(hexCharsUpperLower)-1)
-
 bool
 NS_UnescapeURL(const char* aStr, int32_t aLen, uint32_t aFlags,
                nsACString& aResult)
 {
   bool didAppend = false;
   nsresult rv = NS_UnescapeURL(aStr, aLen, aFlags, aResult, didAppend,
                                mozilla::fallible);
   if (rv == NS_ERROR_OUT_OF_MEMORY) {
@@ -545,67 +544,86 @@ NS_UnescapeURL(const char* aStr, int32_t
   if (!aStr) {
     MOZ_ASSERT_UNREACHABLE("null pointer");
     return NS_ERROR_INVALID_ARG;
   }
 
   MOZ_ASSERT(aResult.IsEmpty(),
              "Passing a non-empty string as an out parameter!");
 
+  uint32_t len;
   if (aLen < 0) {
-    aLen = strlen(aStr);
+    size_t stringLength = strlen(aStr);
+    if (stringLength >= UINT32_MAX) {
+      return NS_ERROR_OUT_OF_MEMORY;
+    }
+    len = stringLength;
+  } else {
+    len = aLen;
   }
 
   bool ignoreNonAscii = !!(aFlags & esc_OnlyASCII);
   bool ignoreAscii = !!(aFlags & esc_OnlyNonASCII);
   bool writing = !!(aFlags & esc_AlwaysCopy);
   bool skipControl = !!(aFlags & esc_SkipControl);
   bool skipInvalidHostChar = !!(aFlags & esc_Host);
 
+  unsigned char* destPtr;
+  uint32_t destPos;
+
   if (writing) {
-    if (!aResult.SetCapacity(aLen, mozilla::fallible)) {
+    if (!aResult.SetLength(len, mozilla::fallible)) {
       return NS_ERROR_OUT_OF_MEMORY;
     }
+    destPos = 0;
+    destPtr = reinterpret_cast<unsigned char*>(aResult.BeginWriting());
   }
 
   const char* last = aStr;
-  const char* p = aStr;
+  const char* end = aStr + len;
 
-  for (int i = 0; i < aLen; ++i, ++p) {
-    if (*p == HEX_ESCAPE && i < aLen - 2) {
+  for (const char* p = aStr; p < end; ++p) {
+    if (*p == HEX_ESCAPE && p + 2 < end) {
       unsigned char c1 = *((unsigned char*)p + 1);
       unsigned char c2 = *((unsigned char*)p + 2);
       unsigned char u = (UNHEX(c1) << 4) + UNHEX(c2);
-      if (ISHEX(c1) && ISHEX(c2) &&
+      if (mozilla::IsAsciiHexDigit(c1) && mozilla::IsAsciiHexDigit(c2) &&
           (!skipInvalidHostChar || dontNeedEscape(u, aFlags) || c1 >= '8') &&
           ((c1 < '8' && !ignoreAscii) || (c1 >= '8' && !ignoreNonAscii)) &&
           !(skipControl &&
             (c1 < '2' || (c1 == '7' && (c2 == 'f' || c2 == 'F'))))) {
-        if (!writing) {
+        if (MOZ_UNLIKELY(!writing)) {
           writing = true;
-          if (!aResult.SetCapacity(aLen, mozilla::fallible)) {
+          if (!aResult.SetLength(len, mozilla::fallible)) {
             return NS_ERROR_OUT_OF_MEMORY;
           }
+          destPos = 0;
+          destPtr = reinterpret_cast<unsigned char*>(aResult.BeginWriting());
         }
         if (p > last) {
-          if (!aResult.Append(last, p - last, mozilla::fallible)) {
-            return NS_ERROR_OUT_OF_MEMORY;
-          }
+          auto toCopy = p - last;
+          memcpy(destPtr + destPos, last, toCopy);
+          destPos += toCopy;
+          MOZ_ASSERT(destPos <= len);
           last = p;
         }
-        if (!aResult.Append(u, mozilla::fallible)) {
-          return NS_ERROR_OUT_OF_MEMORY;
-        }
-        i += 2;
+        destPtr[destPos] = u;
+        destPos += 1;
+        MOZ_ASSERT(destPos <= len);
         p += 2;
         last += 3;
       }
     }
   }
-  if (writing && last < aStr + aLen) {
-    if (!aResult.Append(last, aStr + aLen - last, mozilla::fallible)) {
-      return NS_ERROR_OUT_OF_MEMORY;
-    }
+  if (writing && last < end) {
+    auto toCopy = end - last;
+    memcpy(destPtr + destPos, last, toCopy);
+    destPos += toCopy;
+    MOZ_ASSERT(destPos <= len);
+  }
+
+  if (writing) {
+    aResult.Truncate(destPos);
   }
 
   aDidAppend = writing;
   return NS_OK;
 }