Bug 125608: Reduce size of nsStandardURLs by average of 27+ bytes. r=bz
authorRandell Jesup <rjesup@wgate.com>
Tue, 07 Jun 2011 12:17:40 -0400
changeset 70690 a44485cd16827d2a7c20aa6f2d6aef16b64cc9a9
parent 70689 e37998002eb52c1d5cf48619d0e78ba719b41467
child 70691 7f4c811a19f59742adaefc2a6b00677f2ea64792
push id1
push userroot
push dateMon, 20 Oct 2014 17:29:22 +0000
reviewersbz
bugs125608
milestone7.0a1
Bug 125608: Reduce size of nsStandardURLs by average of 27+ bytes. r=bz
netwerk/base/src/nsStandardURL.cpp
netwerk/base/src/nsStandardURL.h
--- a/netwerk/base/src/nsStandardURL.cpp
+++ b/netwerk/base/src/nsStandardURL.cpp
@@ -172,18 +172,22 @@ nsSegmentEncoder::nsSegmentEncoder(const
 {
 }
 
 PRInt32 nsStandardURL::
 nsSegmentEncoder::EncodeSegmentCount(const char *str,
                                      const URLSegment &seg,
                                      PRInt16 mask,
                                      nsAFlatCString &result,
-                                     PRBool &appended)
+                                     PRBool &appended,
+                                     PRUint32 extraLen)
 {
+    // extraLen is characters outside the segment that will be 
+    // added when the segment is not empty (like the @ following
+    // a username).
     appended = PR_FALSE;
     if (!str)
         return 0;
     PRInt32 len = 0;
     if (seg.mLen > 0) {
         PRUint32 pos = seg.mPos;
         len = seg.mLen;
 
@@ -215,16 +219,17 @@ nsSegmentEncoder::EncodeSegmentCount(con
             len = result.Length() - initLen;
             appended = PR_TRUE;
         }
         else if (str == encBuf.get()) {
             result += encBuf; // append only!!
             len = encBuf.Length();
             appended = PR_TRUE;
         }
+        len += extraLen;
     }
     return len;
 }
 
 const nsACString &nsStandardURL::
 nsSegmentEncoder::EncodeSegment(const nsASingleFragmentCString &str,
                                 PRInt16 mask,
                                 nsAFlatCString &result)
@@ -493,41 +498,67 @@ nsStandardURL::BuildNormalizedSpec(const
     // passed to this function.
 
     // buffers for holding escaped url segments (these will remain empty unless
     // escaping is required).
     nsCAutoString encUsername, encPassword, encHost, encDirectory,
       encBasename, encExtension, encParam, encQuery, encRef;
     PRBool useEncUsername, useEncPassword, useEncHost, useEncDirectory,
       useEncBasename, useEncExtension, useEncParam, useEncQuery, useEncRef;
+    nsCAutoString portbuf;
 
     //
     // escape each URL segment, if necessary, and calculate approximate normalized
     // spec length.
     //
-    PRUint32 approxLen = 3; // includes room for "://"
+    // [scheme://][username[:password]@]host[:port]/path[;param][?query_string][#ref]
+
+    PRUint32 approxLen = 0;
 
     // the scheme is already ASCII
     if (mScheme.mLen > 0)
-        approxLen += mScheme.mLen;
+        approxLen += mScheme.mLen + 3; // includes room for "://";
 
     // encode URL segments; convert UTF-8 to origin charset and possibly escape.
     // results written to encXXX variables only if |spec| is not already in the
     // appropriate encoding.
     {
         GET_SEGMENT_ENCODER(encoder);
         GET_QUERY_ENCODER(queryEncoder);
-        approxLen += encoder.EncodeSegmentCount(spec, mUsername,  esc_Username,      encUsername,  useEncUsername);
-        approxLen += encoder.EncodeSegmentCount(spec, mPassword,  esc_Password,      encPassword,  useEncPassword);
-        approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory,     encDirectory, useEncDirectory);
+        // Items using an extraLen of 1 don't add anything unless mLen > 0
+        // Username@
+        approxLen += encoder.EncodeSegmentCount(spec, mUsername,  esc_Username,      encUsername,  useEncUsername, 1);
+        // :Password
+        approxLen += encoder.EncodeSegmentCount(spec, mPassword,  esc_Password,      encPassword,  useEncPassword, 1);
+        // mHost is handled differently below due to encoding differences
+        NS_ABORT_IF_FALSE(mPort > 0 || mPort == -1, "Invalid negative mPort");
+        if (mPort != -1 && mPort != mDefaultPort)
+        {
+            // :port
+            portbuf.AppendInt(mPort);
+            approxLen += portbuf.Length() + 1;
+        }
+
+        approxLen += 1; // reserve space for possible leading '/' - may not be needed
+        // Should just use mPath?  These are pessimistic, and thus waste space
+        approxLen += encoder.EncodeSegmentCount(spec, mDirectory, esc_Directory,     encDirectory, useEncDirectory, 1);
         approxLen += encoder.EncodeSegmentCount(spec, mBasename,  esc_FileBaseName,  encBasename,  useEncBasename);
-        approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension);
-        approxLen += encoder.EncodeSegmentCount(spec, mParam,     esc_Param,         encParam,     useEncParam);
-        approxLen += queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query,        encQuery,     useEncQuery);
-        approxLen += encoder.EncodeSegmentCount(spec, mRef,       esc_Ref,           encRef,       useEncRef);
+        approxLen += encoder.EncodeSegmentCount(spec, mExtension, esc_FileExtension, encExtension, useEncExtension, 1);
+
+        // These next ones *always* add their leading character even if length is 0
+        // Handles items like "http://#"
+        // ;param
+        if (mParam.mLen >= 0)
+            approxLen += 1 + encoder.EncodeSegmentCount(spec, mParam,     esc_Param,         encParam,     useEncParam);
+        // ?query
+        if (mQuery.mLen >= 0)
+            approxLen += 1 + queryEncoder.EncodeSegmentCount(spec, mQuery, esc_Query,        encQuery,     useEncQuery);
+        // #ref
+        if (mRef.mLen >= 0)
+            approxLen += 1 + encoder.EncodeSegmentCount(spec, mRef,       esc_Ref,           encRef,       useEncRef);
     }
 
     // do not escape the hostname, if IPv6 address literal, mHost will
     // already point to a [ ] delimited IPv6 address literal.
     // However, perform Unicode normalization on it, as IDN does.
     mHostEncoding = eEncoding_ASCII;
     if (mHost.mLen > 0) {
         const nsCSubstring& tempHost =
@@ -540,17 +571,18 @@ nsStandardURL::BuildNormalizedSpec(const
             approxLen += encHost.Length();
         else
             approxLen += mHost.mLen;
     }
 
     //
     // generate the normalized URL string
     //
-    if (!EnsureStringLength(mSpec, approxLen + 32))
+    // approxLen should be correct or 1 high
+    if (!EnsureStringLength(mSpec, approxLen+1)) // buf needs a trailing '\0' below
         return NS_ERROR_OUT_OF_MEMORY;
     char *buf;
     mSpec.BeginWriting(buf);
     PRUint32 i = 0;
 
     if (mScheme.mLen > 0) {
         i = AppendSegmentToBuf(buf, i, spec, mScheme);
         net_ToLowerCase(buf + mScheme.mPos, mScheme.mLen);
@@ -567,20 +599,20 @@ nsStandardURL::BuildNormalizedSpec(const
             buf[i++] = ':';
             i = AppendSegmentToBuf(buf, i, spec, mPassword, &encPassword, useEncPassword);
         }
         buf[i++] = '@';
     }
     if (mHost.mLen > 0) {
         i = AppendSegmentToBuf(buf, i, spec, mHost, &encHost, useEncHost);
         net_ToLowerCase(buf + mHost.mPos, mHost.mLen);
+        NS_ABORT_IF_FALSE(mPort > 0 || mPort == -1, "Invalid negative mPort");
         if (mPort != -1 && mPort != mDefaultPort) {
-            nsCAutoString portbuf;
-            portbuf.AppendInt(mPort);
             buf[i++] = ':';
+            // Already formatted while building approxLen
             i = AppendToBuf(buf, i, portbuf.get(), portbuf.Length());
         }
     }
 
     // record authority length
     mAuthority.mLen = i - mAuthority.mPos;
 
     // path must always start with a "/"
@@ -658,17 +690,17 @@ nsStandardURL::BuildNormalizedSpec(const
         if (SegmentIs(buf,mScheme,"ftp")) {
             coalesceFlag = (netCoalesceFlags) (coalesceFlag 
                                         | NET_COALESCE_ALLOW_RELATIVE_ROOT
                                         | NET_COALESCE_DOUBLE_SLASH_IS_ROOT);
         }
         CoalescePath(coalesceFlag, buf + mDirectory.mPos);
     }
     mSpec.SetLength(strlen(buf));
-    NS_ASSERTION(mSpec.Length() <= approxLen+32, "We've overflowed the mSpec buffer!");
+    NS_ASSERTION(mSpec.Length() <= approxLen, "We've overflowed the mSpec buffer!");
     return NS_OK;
 }
 
 PRBool
 nsStandardURL::SegmentIs(const URLSegment &seg, const char *val, PRBool ignoreCase)
 {
     // one or both may be null
     if (!val || mSpec.IsEmpty())
@@ -1490,16 +1522,20 @@ nsStandardURL::SetPort(PRInt32 port)
 {
     ENSURE_MUTABLE();
 
     LOG(("nsStandardURL::SetPort [port=%d]\n", port));
 
     if ((port == mPort) || (mPort == -1 && port == mDefaultPort))
         return NS_OK;
 
+    // ports must be >= 0 (and 0 is pretty much garbage too, though legal per RFC)
+    if (port <= 0 && port != -1) // -1 == use default
+        return NS_ERROR_MALFORMED_URI;
+
     if (mURLType == URLTYPE_NO_AUTHORITY) {
         NS_WARNING("cannot set port on no-auth url");
         return NS_ERROR_UNEXPECTED;
     }
 
     InvalidateCache();
 
     if (mPort == -1) {
--- a/netwerk/base/src/nsStandardURL.h
+++ b/netwerk/base/src/nsStandardURL.h
@@ -130,17 +130,18 @@ public: /* internal -- HPUX compiler can
 
         // Encode the given segment if necessary, and return the length of
         // the encoded segment.  The encoded segment is appended to |buf|
         // if and only if encoding is required.
         PRInt32 EncodeSegmentCount(const char *str,
                                    const URLSegment &segment,
                                    PRInt16 mask,
                                    nsAFlatCString &buf,
-                                   PRBool& appended);
+                                   PRBool& appended,
+                                   PRUint32 extraLen = 0);
          
         // Encode the given string if necessary, and return a reference to
         // the encoded string.  Returns a reference to |buf| if encoding
         // is required.  Otherwise, a reference to |str| is returned.
         const nsACString &EncodeSegment(const nsASingleFragmentCString &str,
                                         PRInt16 mask,
                                         nsAFlatCString &buf);
     private: