Merge pull request #625 from AtalayaLabs/claude/performance-optimization-analysis-raoezl

Round 16: incremental lanes/contextMap builders & alloc cuts
This commit is contained in:
Dionisio Pozo
2026-07-19 19:35:26 +02:00
committed by GitHub
20 changed files with 1388 additions and 123 deletions
+30 -9
View File
@@ -1153,18 +1153,39 @@ pub(super) fn build_content_disposition(name: &str, mime: &str, force_inline: bo
.remove(b'`')
.remove(b'|')
.remove(b'~');
let encoded = utf8_percent_encode(name, RFC5987_SET).to_string();
// Fast path: a name whose every byte is an RFC 5987 attr-char needs neither
// percent-encoding nor ASCII-fallback filtering ('"' and '\\' are not
// attr-chars, so none is substituted), so `filename` and `filename*` are the
// name verbatim — one allocation (the header) instead of three.
let all_attr_char = name.bytes().all(|b| {
b.is_ascii_alphanumeric()
|| matches!(
b,
b'!' | b'#' | b'$' | b'&' | b'+' | b'-' | b'.' | b'^' | b'_' | b'`' | b'|' | b'~'
)
});
if all_attr_char {
return format!("{disposition}; filename=\"{name}\"; filename*=UTF-8''{name}");
}
let ascii_safe: String = name
.chars()
.filter(|c| c.is_ascii_graphic() || *c == ' ')
.map(|c| match c {
// Slow path: assemble the header in one pre-sized buffer, writing the ASCII
// fallback and the percent-encoded form in place — no throwaway `ascii_safe`
// / `encoded` Strings. Sized for the worst case (every byte → %XX) so it
// never grows.
let mut out = String::with_capacity(disposition.len() + name.len() * 4 + 32);
out.push_str(disposition);
out.push_str("; filename=\"");
for c in name.chars().filter(|c| c.is_ascii_graphic() || *c == ' ') {
out.push(match c {
'"' | '\\' => '_',
_ => c,
})
.collect();
format!("{disposition}; filename=\"{ascii_safe}\"; filename*=UTF-8''{encoded}")
});
}
out.push_str("\"; filename*=UTF-8''");
for chunk in utf8_percent_encode(name, RFC5987_SET) {
out.push_str(chunk);
}
out
}
// ── Route handlers (free functions) ──────────────────────────────────────────
+6 -5
View File
@@ -43,12 +43,13 @@ pub async fn handle_preview(
) -> impl IntoResponse {
// Parse the Nextcloud file ID — the NC app may append an instance suffix
// (e.g. "00000326ocnca"), so strip non-digit characters first.
let numeric_part: String = params
let digit_end = params
.file_id
.chars()
.take_while(|c| c.is_ascii_digit())
.collect();
let nc_file_id: i64 = match numeric_part.parse() {
.as_bytes()
.iter()
.position(|b| !b.is_ascii_digit())
.unwrap_or(params.file_id.len());
let nc_file_id: i64 = match params.file_id[..digit_end].parse() {
Ok(id) => id,
Err(_) => {
return Response::builder()
+4 -4
View File
@@ -10,7 +10,7 @@ use quick_xml::{
use std::collections::{HashMap, HashSet};
use std::sync::Arc;
use crate::application::dtos::display_helpers::format_file_size;
use crate::application::dtos::display_helpers::{format_file_size, intern_display};
use crate::application::dtos::file_dto::FileDto;
use crate::application::dtos::folder_dto::FolderDto;
use crate::application::dtos::search_dto::SearchCriteriaDto;
@@ -443,9 +443,9 @@ fn folder_dto_from_search(
created_at: sr.created_at,
modified_at: sr.modified_at,
is_root: sr.is_root,
icon_class: Arc::from("fas fa-folder"),
icon_special_class: Arc::from("folder-icon"),
category: Arc::from("Folder"),
icon_class: intern_display("fas fa-folder"),
icon_special_class: intern_display("folder-icon"),
category: intern_display("Folder"),
// §14 provenance not selected by search results.
created_by: None,
updated_by: None,
+20 -12
View File
@@ -186,19 +186,27 @@ pub fn nc_collection_href(username: &str, subpath: &str) -> String {
pub fn nc_href(username: &str, subpath: &str) -> String {
let subpath = subpath.trim_matches('/');
let encoded_user = urlencoding::encode(username);
if subpath.is_empty() {
format!("/remote.php/dav/files/{}/", encoded_user)
} else {
let encoded_segments: Vec<_> = subpath
.split('/')
.map(|seg| urlencoding::encode(seg))
.collect();
format!(
"/remote.php/dav/files/{}/{}",
encoded_user,
encoded_segments.join("/")
)
// Write the prefix, user and each encoded segment straight into one
// pre-sized buffer — avoids the per-segment `Vec<Cow>`, the joined String and
// the `format!` result the previous `.map(...).collect().join("/")` allocated
// on every NC PROPFIND/REPORT href (mirrors the native `encode_uri_path`).
// Keeps `urlencoding::encode` so the emitted bytes are unchanged.
const PREFIX: &str = "/remote.php/dav/files/";
let mut out = String::with_capacity(PREFIX.len() + encoded_user.len() + subpath.len() + 8);
out.push_str(PREFIX);
out.push_str(&encoded_user);
out.push('/');
// No empty-segment filter: `split('/')` on an empty (root) subpath yields a
// single "" whose encode is "" — leaving the trailing slash above intact —
// and any internal "//" is preserved byte-for-byte, exactly as the old
// `split → map → join("/")` produced.
for (i, seg) in subpath.split('/').enumerate() {
if i > 0 {
out.push('/');
}
out.push_str(&urlencoding::encode(seg));
}
out
}
/// Dispatch Nextcloud WebDAV request to the appropriate handler.