uri_normalizer_percent_decode_unreserved() and
uri_normalizer_percent_upper() walked their input one byte at a time,
appending every single byte to the output even when no percent sign was
involved. For URIs that contain few or no escape sequences, which is the
common case, this copies the whole string byte by byte.

Only '%' sequences need per-byte handling. Locate the next '%' with
memchr() and copy the run preceding it in one memcpy(), then handle the
escape sequence exactly as before. The output is unchanged, including
the strict mode error behaviour for truncated or invalid sequences.

This patch and the query sort change that follows were measured
together: on a 24-core AMD EPYC 7413 with a 20 kB query string (427
parameters), gcc 8.5.0 -O2 -march=native and keep-alive, the CPU time
per request spent in the six normalizers drops from 1137.52 us to
1062.53 us (-6.6%). The two were not measured separately; most of that
comes from the next patch, which is why the numbers are quoted there.

This is a pure optimization with no functional change, so the risk of
regression is low. It is not needed in stable branches.

Signed-off-by: Youngkwang Lee <[email protected]>
---
 src/uri_normalizer.c | 113 ++++++++++++++++++++++++++-----------------
 1 file changed, 68 insertions(+), 45 deletions(-)

diff --git a/src/uri_normalizer.c b/src/uri_normalizer.c
index 12317f183..c24d95fd5 100644
--- a/src/uri_normalizer.c
+++ b/src/uri_normalizer.c
@@ -102,40 +102,52 @@ enum uri_normalizer_err 
uri_normalizer_percent_decode_unreserved(const struct is
        }
 
        while (istlen(scanner)) {
-               const char current = istshift(&scanner);
+               /* Only '%' sequences need per-byte handling, everything else is
+                * copied verbatim. Locating the next '%' with memchr() and
+                * copying the run before it in one go avoids walking the input
+                * one byte at a time without changing the output.
+                */
+               const char *pct = memchr(istptr(scanner), '%', istlen(scanner));
+
+               if (!pct) {
+                       memcpy(istend(output), istptr(scanner), 
istlen(scanner));
+                       output = ist2(istptr(output), istlen(output) + 
istlen(scanner));
+                       break;
+               }
 
-               if (current == '%') {
-                       if (istlen(scanner) >= 2) {
-                               if (ishex(istptr(scanner)[0]) && 
ishex(istptr(scanner)[1])) {
-                                       char hex1, hex2, c;
-
-                                       hex1 = istshift(&scanner);
-                                       hex2 = istshift(&scanner);
-                                       c = (hex2i(hex1) << 4) + hex2i(hex2);
-
-                                       if (is_unreserved_character(c)) {
-                                               output = __istappend(output, c);
-                                       }
-                                       else {
-                                               output = __istappend(output, 
current);
-                                               output = __istappend(output, 
hex1);
-                                               output = __istappend(output, 
hex2);
-                                       }
-
-                                       continue;
-                               }
-                       }
+               if (pct != istptr(scanner)) {
+                       const size_t run = pct - istptr(scanner);
 
-                       if (strict) {
-                               err = URI_NORMALIZER_ERR_INVALID_INPUT;
-                               goto fail;
+                       memcpy(istend(output), istptr(scanner), run);
+                       output = ist2(istptr(output), istlen(output) + run);
+                       scanner = istadv(scanner, run);
+               }
+
+               /* scanner now starts on the '%'. */
+               istshift(&scanner);
+
+               if (istlen(scanner) >= 2 && ishex(istptr(scanner)[0]) && 
ishex(istptr(scanner)[1])) {
+                       char hex1, hex2, c;
+
+                       hex1 = istshift(&scanner);
+                       hex2 = istshift(&scanner);
+                       c = (hex2i(hex1) << 4) + hex2i(hex2);
+
+                       if (is_unreserved_character(c)) {
+                               output = __istappend(output, c);
                        }
                        else {
-                               output = __istappend(output, current);
+                               output = __istappend(output, '%');
+                               output = __istappend(output, hex1);
+                               output = __istappend(output, hex2);
                        }
                }
+               else if (strict) {
+                       err = URI_NORMALIZER_ERR_INVALID_INPUT;
+                       goto fail;
+               }
                else {
-                       output = __istappend(output, current);
+                       output = __istappend(output, '%');
                }
        }
 
@@ -171,28 +183,39 @@ enum uri_normalizer_err 
uri_normalizer_percent_upper(const struct ist input, int
        }
 
        while (istlen(scanner)) {
-               const char current = istshift(&scanner);
+               /* Bulk-copy the run before the next '%', see
+                * uri_normalizer_percent_decode_unreserved().
+                */
+               const char *pct = memchr(istptr(scanner), '%', istlen(scanner));
+
+               if (!pct) {
+                       memcpy(istend(output), istptr(scanner), 
istlen(scanner));
+                       output = ist2(istptr(output), istlen(output) + 
istlen(scanner));
+                       break;
+               }
 
-               if (current == '%') {
-                       if (istlen(scanner) >= 2) {
-                               if (ishex(istptr(scanner)[0]) && 
ishex(istptr(scanner)[1])) {
-                                       output = __istappend(output, current);
-                                       output = __istappend(output, 
toupper((unsigned char)istshift(&scanner)));
-                                       output = __istappend(output, 
toupper((unsigned char)istshift(&scanner)));
-                                       continue;
-                               }
-                       }
+               if (pct != istptr(scanner)) {
+                       const size_t run = pct - istptr(scanner);
 
-                       if (strict) {
-                               err = URI_NORMALIZER_ERR_INVALID_INPUT;
-                               goto fail;
-                       }
-                       else {
-                               output = __istappend(output, current);
-                       }
+                       memcpy(istend(output), istptr(scanner), run);
+                       output = ist2(istptr(output), istlen(output) + run);
+                       scanner = istadv(scanner, run);
+               }
+
+               /* scanner now starts on the '%'. */
+               istshift(&scanner);
+
+               if (istlen(scanner) >= 2 && ishex(istptr(scanner)[0]) && 
ishex(istptr(scanner)[1])) {
+                       output = __istappend(output, '%');
+                       output = __istappend(output, toupper((unsigned 
char)istshift(&scanner)));
+                       output = __istappend(output, toupper((unsigned 
char)istshift(&scanner)));
+               }
+               else if (strict) {
+                       err = URI_NORMALIZER_ERR_INVALID_INPUT;
+                       goto fail;
                }
                else {
-                       output = __istappend(output, current);
+                       output = __istappend(output, '%');
                }
        }
 
-- 
2.45.1



Reply via email to