Optimize stringify edge case. r=sayrer/jorendorff.
authorBrendan Eich <brendan@mozilla.org>
Fri, 10 Jul 2009 15:13:54 -0400
changeset 26059 4c0647472a578150b9f5264810cd20bcacef1fd7
parent 26058 6b3d980f794a7b61345ee145056e042d1d41d97c
child 26060 79292a9fdeb1bfda94750fb77de64cf4bf1ca2ad
push id1767
push userrsayre@mozilla.com
push dateTue, 14 Jul 2009 17:24:20 +0000
reviewerssayrer, jorendorff
milestone1.9.1.1pre
Optimize stringify edge case. r=sayrer/jorendorff.
js/src/json.cpp
js/src/jsscan.cpp
--- a/js/src/json.cpp
+++ b/js/src/json.cpp
@@ -221,17 +221,18 @@ public:
     StringifyContext(JSONWriteCallback callback, JSObject *replacer, void *data)
     : callback(callback), replacer(replacer), data(data), depth(0)
     {
         js_InitStringBuffer(&gap);
     }
 
     ~StringifyContext()
     {
-        js_FinishStringBuffer(&gap);
+        if (STRING_BUFFER_OK(&gap))
+            js_FinishStringBuffer(&gap);
     }
 
     JSONWriteCallback callback;
     JSStringBuffer gap;
     JSObject *replacer;
     void *data;
     uint32 depth;
 };
--- a/js/src/jsscan.cpp
+++ b/js/src/jsscan.cpp
@@ -671,44 +671,51 @@ js_ReportCompileErrorNumber(JSContext *c
         /* Set the error flag to suppress spurious reports. */
         ts->flags |= TSF_ERROR;
     }
 
     return warning;
 }
 
 static JSBool
-GrowStringBuffer(JSStringBuffer *sb, size_t newlength)
+GrowStringBuffer(JSStringBuffer *sb, size_t amount)
 {
-    ptrdiff_t offset;
-    jschar *bp;
+    ptrdiff_t offset = sb->ptr - sb->base;
+    JS_ASSERT(offset >= 0);
 
-    offset = PTRDIFF(sb->ptr, sb->base, jschar);
-    JS_ASSERT(offset >= 0);
-    newlength += offset + 1;
+    /*
+     * This addition needs an overflow check, but we can defer bounding against
+     * ~size_t(0) / sizeof(jschar) till later to consolidate that test.
+     */
+    size_t newlength = offset + amount + 1;
+    if (size_t(offset) < newlength) {
+        /* Grow by powers of two until 16MB, then grow by that chunk size. */
+        const size_t CHUNK_SIZE_MASK = JS_BITMASK(24);
 
-    /* Grow by powers of two until 16MB, then grow by that chunk size. */
-    const size_t CHUNK_SIZE = JS_BIT(24);
-    if (newlength < CHUNK_SIZE)
-        newlength = JS_BIT(JS_CeilingLog2(newlength));
-    else
-        newlength = JS_ROUNDUP(newlength, CHUNK_SIZE);
-    if ((size_t)offset < newlength && newlength < ~(size_t)0 / sizeof(jschar))
-        bp = (jschar *) realloc(sb->base, newlength * sizeof(jschar));
-    else
-        bp = NULL;
-    if (!bp) {
-        free(sb->base);
-        sb->base = STRING_BUFFER_ERROR_BASE;
-        return JS_FALSE;
+        if (newlength <= CHUNK_SIZE_MASK)
+            newlength = JS_BIT(JS_CeilingLog2(newlength));
+        else if (newlength & CHUNK_SIZE_MASK)
+            newlength = (newlength | CHUNK_SIZE_MASK) + 1;
+
+        /* Now do the full overflow check. */
+        if (size_t(offset) < newlength && newlength < ~size_t(0) / sizeof(jschar)) {
+            jschar *bp = (jschar *) realloc(sb->base, newlength * sizeof(jschar));
+            if (bp) {
+                sb->base = bp;
+                sb->ptr = bp + offset;
+                sb->limit = bp + newlength - 1;
+                return true;
+            }
+        }
     }
-    sb->base = bp;
-    sb->ptr = bp + offset;
-    sb->limit = bp + newlength - 1;
-    return JS_TRUE;
+
+    /* Either newlength overflow or realloc failure: poison the well. */
+    free(sb->base);
+    sb->base = STRING_BUFFER_ERROR_BASE;
+    return false;
 }
 
 static void
 FreeStringBuffer(JSStringBuffer *sb)
 {
     JS_ASSERT(STRING_BUFFER_OK(sb));
     if (sb->base)
         free(sb->base);