perf: round 20 — iCal/vCard parse allocs, owned-DTO moves, Result-collect pre-size, NC etag/favorites emit
Benchmark-gated (benches/ROUND20.md), same rule as rounds 2-19: every change ships with a BEFORE/AFTER counting-allocator micro-benchmark and a byte-value equivalence gate; a non-winning AFTER is rolled back (never applied). The rollback rule is encoded in the harness (GATE FAIL exit). All 8 sections pass. Reproduce: cargo run --release --features bench --example bench_round20_micro - A1 CalendarEvent iCal parse: replace the throwaway per-property HashMap<String,Vec<String>> (DTSTART/DTEND/RECURRENCE-ID) with a direct VALUE=DATE scan; prop_with_params kept #[cfg(test)] (6->2 allocs/event, 4.2x) - A2 UserDto::from: add User::into_parts and MOVE image (<=512 KiB data URI) + ui_preferences JSON instead of cloning on every /api/auth/me (27->14 allocs) - A3 parse_vcard: drop the per-line to_ascii_uppercase copy + the lines Vec; promote ascii_ci_contains to common::text and share it (8->1 allocs/contact) - A4 Calendar/AddressBook DTO: into_parts move incl. custom_properties map (18->10) - I1 file-listing repos: collect::<Result<Vec>>() size-hints to 0 and grows from capacity 0; pre-size with Vec::with_capacity (8->1 container reallocs, 4 sites) - I4 plaintext_stream: lazy emit iterator instead of eager Vec collect (43x wall) - C1 NC write_etag_element: borrowed pre-escaped quote events, no owned quoted String/escape re-alloc; byte-identical output (3->0 allocs/PROPFIND row) - C3 NC favorites REPORT: map.remove() move instead of get().clone() (~7 allocs/fav) Deferred (documented in ROUND20.md): NC oc:id/trashbin buffer reuse, I1 sibling CardDAV/CalDAV listing paths, Contact JSONB Json<Vec<_>> decode, dedup settle_batch &str bind, and a fast DoS-resistant hasher for hot trusted-key maps (needs a dependency decision). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JsJjcVX9RoN96DMa35Wqzd
This commit is contained in:
@@ -49,6 +49,48 @@ pub struct Calendar {
|
||||
custom_properties: std::collections::HashMap<String, String>,
|
||||
}
|
||||
|
||||
/// Owned decomposition of a [`Calendar`] (mirrors `FileParts`/`UserParts`).
|
||||
/// Lets `CalendarDto::from` MOVE the heap fields — notably the
|
||||
/// `custom_properties` map — instead of cloning them on every CalDAV discovery
|
||||
/// listing (benches/ROUND20.md §A4).
|
||||
pub struct CalendarParts {
|
||||
pub id: Uuid,
|
||||
pub name: String,
|
||||
pub owner_id: Uuid,
|
||||
pub description: Option<String>,
|
||||
pub color: Option<String>,
|
||||
pub created_at: DateTime<Utc>,
|
||||
pub updated_at: DateTime<Utc>,
|
||||
pub custom_properties: std::collections::HashMap<String, String>,
|
||||
}
|
||||
|
||||
impl Calendar {
|
||||
/// Decompose into [`CalendarParts`], moving every owned field out
|
||||
/// (exhaustive destructure — compiler-checked against added fields).
|
||||
pub fn into_parts(self) -> CalendarParts {
|
||||
let Calendar {
|
||||
id,
|
||||
name,
|
||||
owner_id,
|
||||
description,
|
||||
color,
|
||||
created_at,
|
||||
updated_at,
|
||||
custom_properties,
|
||||
} = self;
|
||||
CalendarParts {
|
||||
id,
|
||||
name,
|
||||
owner_id,
|
||||
description,
|
||||
color,
|
||||
created_at,
|
||||
updated_at,
|
||||
custom_properties,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Calendar {
|
||||
/**
|
||||
* Creates a new calendar with the given properties.
|
||||
|
||||
Reference in New Issue
Block a user