Skip to content

perf(dpi/quic): use Cow<'static, str> for known QUIC version strings#401

Open
obchain wants to merge 1 commit into
domcyrus:mainfrom
obchain:perf/quic-version-string-cow
Open

perf(dpi/quic): use Cow<'static, str> for known QUIC version strings#401
obchain wants to merge 1 commit into
domcyrus:mainfrom
obchain:perf/quic-version-string-cow

Conversation

@obchain

@obchain obchain commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Closes #400

What

QuicInfo::new(version) is called on every QUIC long-header packet. The internal quic_version_to_string helper returned Option<String>, allocating a heap String for each of the seven well-known version identifiers (v1, v2, draft-27/28/29, Q050, T050) — even though they are compile-time constants.

This PR changes QuicInfo.version_string from Option<String> to Option<Cow<'static, str>> and returns Cow::Borrowed for the static arms:

// before
fn quic_version_to_string(version: u32) -> Option<String> {
    match version {
        0x00000001 => Some("v1".to_string()),       // heap alloc
        0x6b3343cf => Some("v2".to_string()),       // heap alloc}
}

// after
fn quic_version_to_string(version: u32) -> Option<Cow<'static, str>> {
    match version {
        0x00000001 => Some(Cow::Borrowed("v1")),    // zero alloc
        0x6b3343cf => Some(Cow::Borrowed("v2")),    // zero alloc
        …
        v if (v >> 8) == 0xff0000 => Some(Cow::Owned(format!("draft-{}", v & 0xff))),
        _ => None,
    }
}

QuicInfo derives only Debug + Clone, so the field-type change requires no serde or macro adjustments. Cow::clone() for a Borrowed variant is a pointer copy, so the merge-path clone also becomes cheaper.

The single display callsite in details.rs changes from .as_ref() + .clone() to .as_deref() + .to_owned(), which is semantically identical.

Test

A new unit test quic_version_string_known_versions_are_borrowed asserts:

  • All seven known versions produce Cow::Borrowed — enforcing the no-alloc guarantee at the type level.
  • An unknown version produces None.
  • The dynamic draft-N arm produces Cow::Owned with the correct label.

Impact

Per QUIC long-header packet with a known version: −1 heap allocation. Under sustained HTTP/3 traffic (QUIC v1) this path is hot — every packet avoids a small-string allocation and its associated allocator overhead.

Verification

cargo build --workspace   # clean
cargo test -p rustnet-core network::dpi::quic  # 12 passed
cargo test -p rustnet-core quic_version_string  # 1 passed
cargo fmt --check         # clean
cargo clippy --all-targets -- -D warnings  # clean

QuicInfo::new() is called on every QUIC long-header packet. The internal
quic_version_to_string helper returned Option<String>, allocating a heap
String for each of the seven well-known version identifiers (v1, v2,
draft-27/28/29, Q050, T050) even though they are compile-time constants.

Change the field type to Option<Cow<'static, str>> and return
Cow::Borrowed for the static arms, Cow::Owned only for the dynamic
draft-N case. Cow::clone() for a Borrowed variant is a pointer copy, so
the merge-path clone becomes cheaper too.

The one display callsite in details.rs changes from .as_ref() + .clone()
to .as_deref() + .to_owned(), which is semantically identical.

A unit test asserts the Borrowed invariant for all seven known versions
and verifies the Owned path for the dynamic draft-N arm.

Closes domcyrus#400
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

perf(dpi/quic): avoid heap allocation for known QUIC version strings

1 participant