From 1ca32cad437da18ba647dcbb685f51852bcb3f47 Mon Sep 17 00:00:00 2001 From: Paolo Barbolini Date: Sun, 29 Dec 2024 11:42:51 +0100 Subject: [PATCH] refactor: reduce number of string allocations when signing requests --- src/bucket.rs | 2 +- src/signing/canonical_request.rs | 38 ++++++++++++++++++-------------- src/signing/util.rs | 10 +++++---- 3 files changed, 29 insertions(+), 21 deletions(-) diff --git a/src/bucket.rs b/src/bucket.rs index 902b5e2..4e20b99 100644 --- a/src/bucket.rs +++ b/src/bucket.rs @@ -138,7 +138,7 @@ impl Bucket { /// # Errors /// Returns a `ParseError` if the object is not a valid path. pub fn object_url(&self, object: &str) -> Result { - let object = percent_encode_path(object); + let object: Cow<'_, str> = percent_encode_path(object).into(); self.base_url.join(&object) } } diff --git a/src/signing/canonical_request.rs b/src/signing/canonical_request.rs index 02f318f..28b01e6 100644 --- a/src/signing/canonical_request.rs +++ b/src/signing/canonical_request.rs @@ -1,3 +1,5 @@ +use std::fmt; + use url::Url; use super::util::percent_encode; @@ -23,15 +25,15 @@ where string.push_str(url.path()); string.push('\n'); - canonical_query_string(query_string, &mut string); + canonical_query_string(query_string, &mut string).expect("String writer panicked"); string.push('\n'); - canonical_headers(headers, &mut string); + canonical_headers(headers, &mut string).expect("String writer panicked"); string.push('\n'); - signed_headers_(signed_headers, &mut string); + signed_headers_(signed_headers, &mut string).expect("String writer panicked"); string.push('\n'); @@ -40,7 +42,7 @@ where string } -fn canonical_query_string<'a, Q>(query_string: Q, string: &mut String) +fn canonical_query_string<'a, Q>(query_string: Q, mut out: impl fmt::Write) -> fmt::Result where Q: Iterator, { @@ -49,29 +51,31 @@ where if first { first = false; } else { - string.push('&'); + out.write_char('&')?; } - string.push_str(&percent_encode(key)); - string.push('='); - string.push_str(&percent_encode(val)); + write!(out, "{}={}", percent_encode(key), percent_encode(val))?; } + + Ok(()) } -fn canonical_headers<'a, H>(headers: H, string: &mut String) +fn canonical_headers<'a, H>(headers: H, mut out: impl fmt::Write) -> fmt::Result where H: Iterator, { for (key, val) in headers { - string.push_str(key); - string.push(':'); - string.push_str(val.trim()); + out.write_str(key)?; + out.write_char(':')?; + out.write_str(val.trim())?; - string.push('\n'); + out.write_char('\n')?; } + + Ok(()) } -fn signed_headers_<'a, H>(signed_headers: H, string: &mut String) +fn signed_headers_<'a, H>(signed_headers: H, mut out: impl fmt::Write) -> fmt::Result where H: Iterator, { @@ -80,11 +84,13 @@ where if first { first = false; } else { - string.push(';'); + out.write_char(';')?; } - string.push_str(key); + out.write_str(key)?; } + + Ok(()) } #[cfg(test)] diff --git a/src/signing/util.rs b/src/signing/util.rs index b255181..3d3eea8 100644 --- a/src/signing/util.rs +++ b/src/signing/util.rs @@ -1,3 +1,5 @@ +use std::{borrow::Cow, fmt::Display}; + use percent_encoding::{utf8_percent_encode, AsciiSet, CONTROLS}; use url::Url; @@ -36,12 +38,12 @@ pub const FRAGMENT: &AsciiSet = &CONTROLS pub const FRAGMENT_SLASH: &AsciiSet = &FRAGMENT.add(b'/'); -pub fn percent_encode(val: &str) -> String { - utf8_percent_encode(val, FRAGMENT_SLASH).to_string() +pub fn percent_encode(val: &str) -> impl Display + Into> + '_ { + utf8_percent_encode(val, FRAGMENT_SLASH) } -pub fn percent_encode_path(val: &str) -> String { - utf8_percent_encode(val, FRAGMENT).to_string() +pub fn percent_encode_path(val: &str) -> impl Display + Into> + '_ { + utf8_percent_encode(val, FRAGMENT) } pub fn add_query_params<'a, Q>(mut url: Url, params: Q) -> Url