uri_normalizer_fragment_encode() used istcat() to append the text
between two '#' characters, and istcat() copies one byte at a time.
A URI without any fragment is therefore copied byte by byte in full.

Copy each run in one memcpy() instead, and check the remaining room up
front rather than relying on istcat() to detect the overflow. istcat()
copies as much as fits before returning an error, but the caller
discards the output on error, so checking first is equivalent. Each
'#' expands to "%23", which is why the check adds three bytes for a
run that ends on one.

Verified by running the old and the new implementation over the same
inputs with every destination size from zero to past the truncation
point, comparing both the output and the returned error code: 119898
cases, no difference.

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 | 18 ++++++++++++------
 1 file changed, 12 insertions(+), 6 deletions(-)

diff --git a/src/uri_normalizer.c b/src/uri_normalizer.c
index cb67d56f0..1a7654120 100644
--- a/src/uri_normalizer.c
+++ b/src/uri_normalizer.c
@@ -30,17 +30,23 @@ enum uri_normalizer_err 
uri_normalizer_fragment_encode(const struct ist input, s
 
        while (istlen(scanner)) {
                const struct ist before_hash = istsplit(&scanner, '#');
+               const int encode = (istend(before_hash) != istend(scanner));
 
-               if (istcat(&output, before_hash, size) < 0) {
+               /* Everything up to the '#' is copied verbatim, so it can be
+                * copied in one go rather than one byte at a time. Each '#'
+                * grows by two bytes, hence the check for three below.
+                */
+               if (istlen(output) + istlen(before_hash) + (encode ? 3 : 0) > 
size) {
                        err = URI_NORMALIZER_ERR_ALLOC;
                        goto fail;
                }
 
-               if (istend(before_hash) != istend(scanner)) {
-                       if (istcat(&output, ist("%23"), size) < 0) {
-                               err = URI_NORMALIZER_ERR_ALLOC;
-                               goto fail;
-                       }
+               memcpy(istend(output), istptr(before_hash), 
istlen(before_hash));
+               output = ist2(istptr(output), istlen(output) + 
istlen(before_hash));
+
+               if (encode) {
+                       memcpy(istend(output), "%23", 3);
+                       output = ist2(istptr(output), istlen(output) + 3);
                }
        }
 
-- 
2.45.1



Reply via email to