Skip to content

Oscarld/obfuscation http parity#1684

Draft
Eldolfin wants to merge 54 commits intomainfrom
oscarld/obfuscation-http-parity
Draft

Oscarld/obfuscation http parity#1684
Eldolfin wants to merge 54 commits intomainfrom
oscarld/obfuscation-http-parity

Conversation

@Eldolfin
Copy link
Contributor

@Eldolfin Eldolfin commented Mar 6, 2026

What does this PR do?

Ran a fuzzer to find output difference between this obfuscator and the agent's obfuscator, fixed issues one by one, even the nonsensical edge cases.

Motivation

Reach 100% parity between obfuscation libs.

Additional Notes

Anything else we should know when reviewing?

How to test the change?

Describe here in detail how the change can be validated.

@Eldolfin Eldolfin requested a review from Copilot March 6, 2026 16:05
@github-actions
Copy link

github-actions bot commented Mar 6, 2026

Clippy Allow Annotation Report

Comparing clippy allow annotations between branches:

  • Base Branch: origin/main
  • PR Branch: origin/oscarld/obfuscation-http-parity

Summary by Rule

Rule Base Branch PR Branch Change
expect_used 0 1 ⚠️ +1 (N/A)
unwrap_used 1 1 No change (0%)
Total 1 2 ⚠️ +1 (+100.0%)

Annotation Counts by File

File Base Branch PR Branch Change
libdd-trace-obfuscation/src/http.rs 1 2 ⚠️ +1 (+100.0%)

Annotation Stats by Crate

Crate Base Branch PR Branch Change
clippy-annotation-reporter 5 5 No change (0%)
datadog-ffe-ffi 1 1 No change (0%)
datadog-ipc 27 27 No change (0%)
datadog-live-debugger 6 6 No change (0%)
datadog-live-debugger-ffi 10 10 No change (0%)
datadog-profiling-replayer 4 4 No change (0%)
datadog-remote-config 3 3 No change (0%)
datadog-sidecar 59 59 No change (0%)
libdd-common 10 10 No change (0%)
libdd-common-ffi 12 12 No change (0%)
libdd-crashtracker 12 12 No change (0%)
libdd-data-pipeline 5 5 No change (0%)
libdd-ddsketch 2 2 No change (0%)
libdd-dogstatsd-client 1 1 No change (0%)
libdd-profiling 13 13 No change (0%)
libdd-telemetry 19 19 No change (0%)
libdd-tinybytes 4 4 No change (0%)
libdd-trace-normalization 2 2 No change (0%)
libdd-trace-obfuscation 9 10 ⚠️ +1 (+11.1%)
libdd-trace-utils 15 15 No change (0%)
Total 219 220 ⚠️ +1 (+0.5%)

About This Report

This report tracks Clippy allow annotations for specific rules, showing how they've changed in this PR. Decreasing the number of these annotations generally improves code quality.

Eldolfin added 12 commits March 6, 2026 17:09
Go's url.Parse rejects bare '%' and other invalid percent-encoding
sequences, returning an error which causes obfuscateURLString to
return "?". The url crate silently re-encodes them as '%25', so
add an explicit pre-check matching Go's behavior.

Fixes fuzzing testcase: http_fuzzing_594901251
Go's url.Parse stores "." and ".." path segments literally, while the
url crate's join() resolves them via RFC 3986 normalization (making them
empty after stripping the base). Return the original input when
go_like_reference returns empty for a non-empty input that already
passed all error checks (control chars, invalid percent-encoding).

Fixes fuzzing testcase: http_fuzzing_3638045804
Go's url.Parse succeeds for relative URLs (like "0") and applies
path-digit removal to them. The Rust code was returning early from
the go_like_reference path without applying digit removal.

Add remove_relative_path_digits() helper and call it for relative URL
results when remove_path_digits=true.

Fixes fuzzing testcase: http_fuzzing_1928485962
…URLs

Go's url.shouldEscape for encodePath does not allow !, ', (, ), * even though
RFC 3986 considers them valid sub-delimiters in path segments. The url crate
follows RFC 3986 and keeps them unencoded. Post-process go_like_reference
output to encode these characters to match Go's behavior.

Fixes fuzzing testcase: http_fuzzing_4273565798
Go's validEncoded() has an explicit allowlist for !, ', (, ), * so these
are only re-encoded when the path has non-ASCII chars (which forces Go to
call escape() instead of using RawPath). For pure-ASCII inputs, Go's
EscapedPath() returns the RawPath unchanged, keeping ! as-is.

Only apply encode_go_path_chars() when the original input contains non-ASCII.

Fixes fuzzing testcase: http_fuzzing_1457007156
Go's url.Parse percent-encodes non-ASCII chars in fragments (e.g., '#ჸ' →
'#%E1%83%B8'). Our early-return fragment handler was returning the raw
fragment without encoding. Delegate non-empty fragments to go_like_reference
which uses the url crate's join() to correctly encode them.

Fixes fuzzing testcase: http_fuzzing_1092426409
…e error)

Go's url.Parse rejects ":" (missing protocol scheme) and "1:b" (first path
segment cannot contain colon per RFC 3986 §4.2). The url crate accepts them
as path characters. Add an explicit check to return "?" for these inputs.

Fixes fuzzing testcase: http_fuzzing_3119724369
Go's url.Parse percent-encodes control chars in fragments (e.g., '#\x01' →
'#%01'). The url crate silently drops them from fragments, returning '#'.
Pre-encode control bytes in the fragment manually before passing to
go_like_reference via base.join().

Fixes fuzzing testcase: http_fuzzing_1323831861
The previous fix iterated bytes and used 'b as char' which converts u8 to
a Unicode scalar, corrupting multi-byte sequences like Georgian ჸ.
Iterate over chars instead to preserve multi-byte Unicode correctly.

Fixes fuzzing testcase: http_fuzzing_35626170
@github-actions
Copy link

github-actions bot commented Mar 6, 2026

📚 Documentation Check Results

⚠️ 520 documentation warning(s) found

📦 libdd-trace-obfuscation - 520 warning(s)


Updated: 2026-03-06 16:25:24 UTC | Commit: 176984b | missing-docs job results

Eldolfin added 14 commits March 6, 2026 17:09
Go's shouldEscape always encodes '\', '^', '{', '}', '|', '<', '>', '`',
and ' ' in paths (they're not in validEncoded's allowlist). The url crate
keeps them unencoded. Separate from the '!'-etc. class which are only
encoded when non-ASCII chars trigger the escape() fallback.

Fixes fuzzing testcase: http_fuzzing_618280270
Like '!', '\'', '(', ')', '*', the '[' and ']' characters are in Go's
validEncoded allowlist but get encoded when escape() is called due to
non-ASCII chars in the path.

Fixes fuzzing testcase: http_fuzzing_1505427946
…ath split

The url crate treats '\' as a path separator, consuming it silently.
Go treats '\' as a path character and encodes it as '%5C'.
Pre-encode '\' as '%5C' before calling go_like_reference so base.join()
preserves it rather than using it as a path segment separator.

Fixes fuzzing testcase: http_fuzzing_backslash_unicode
…ring()

Go's url.URL.String() omits a bare '#' with no fragment content.
The url crate keeps it. Strip trailing '#' from go_like_reference results.

Fixes fuzzing testcase: http_fuzzing_2438023093
… ! etc.

When determining whether to encode !, ', (, ), *, [, ] (Cat2 chars),
only check the path portion (before '#') for non-ASCII bytes.
A non-ASCII character in the fragment does not trigger Go's escape()
fallback for the path, so the path chars should stay unencoded.

Fixes fuzzing testcase: http_fuzzing_2729083127
…eference

For input '/ჸ', base.join('/ჸ') resolves to 'https://example.invalid/%E1%83%B8'.
Stripping base_prefix='https://example.invalid/' (with trailing slash) drops
the leading '/'. For inputs starting with '/', use the no-trailing-slash strip
to preserve the leading '/' in the output.

Fixes fuzzing testcase: http_fuzzing_slash_unicode
Go's shouldEscape('#', encodeFragment) returns true, so '#' within a fragment
is encoded as '%23'. The url crate keeps it raw. For input '##', Go returns
'#%23' (second '#' encoded). Pre-encode '#' in fragment content.

Fixes fuzzing testcase: http_fuzzing_3710129001
Go's url.Parse rejects control chars in the PATH (returning '?') but
percent-encodes them in the FRAGMENT. Only check path portion (before '#')
for control char rejection. Pre-encode control chars in the fragment before
calling go_like_reference.

Fixes fuzzing testcase: http_fuzzing_1009954227
encode_go_path_chars was operating on the whole URL string including fragment.
Go's encodeFragment mode allows '!', '(', ')', '*' (shouldEscape returns false).
Stop path-char encoding at '#' so the fragment portion is preserved unchanged.

Fixes fuzzing testcase: http_fuzzing_hash_exclamation
Go's url.Parse rejects invalid percent-encoding sequences even in the
fragment portion. Add the same check to the fragment handler.

Fixes fuzzing testcase: http_fuzzing_578834728
Go's escape() for encodeFragment encodes these chars (they're in validEncoded's
allowlist but not in shouldEscape's 'return false' cases). When non-ASCII
chars trigger the escape() fallback, these also get encoded. Pre-encode them
in the fragment when non-ASCII is detected.

Fixes fuzzing testcase: http_fuzzing_3991369296
…CII fragment

When the URL has non-ASCII chars in the fragment, Go's escape() also encodes
cat2 chars (!, ', (, ), *, [, ]) in the fragment. Apply the same encoding to
the result's fragment portion when the original URL's fragment had non-ASCII.

Fixes fuzzing testcase: http_fuzzing_path_frag_quote
…n-ASCII present

Go's shouldEscape for encodeFragment returns false for \! ( ) * explicitly,
so those are NOT encoded even when escape() is triggered by non-ASCII.
Only ' [ ] (in validEncoded allowlist but not shouldEscape's return-false)
get encoded when non-ASCII chars trigger the escape() fallback.
Eldolfin added 22 commits March 6, 2026 17:09
…ue URIs

- Opaque URIs ending with bare '#' (e.g. "C:#") now strip the empty fragment
  to match Go's url.URL.String() which omits it
- When a URL has control chars in the fragment, also check the path for
  invalid percent-encoding before pre-encoding — previously this branch
  returned early and skipped the path validity check, causing inputs like
  "ჸ#%\u{1}" to return a percent-encoded result instead of "?"
…uote

Go's url.EscapedPath() calls escape() on the whole path whenever
validEncoded() returns false. validEncoded() returns false for any char
not in its explicit allowlist — including '\"' (double-quote). When
escape() is called, it also encodes Category 2 chars (!, ', (, ), *).

Add '\"' to the has_cat1 trigger check so that inputs containing '\"'
in the path also get Category 2 encoding, matching Go's behavior.
Go's url.unescape validates that percent-encoded bytes in path/fragment
form valid UTF-8 sequences. The Rust implementation only checked for
syntactically invalid percent-encoding (wrong hex digits count), missing
cases like %80 (a lone UTF-8 continuation byte) which Go rejects.

Fix: collect consecutive percent-encoded bytes and validate with from_utf8.
Two bugs fixed:
1. go_like_reference() dropped the fragment when stripping the query.
   Fix: after finding path_end (at '?'), extract the '#...' fragment
   and include it in the returned string.
2. obfuscate_url_string() returned '?' for '?#frag' inputs with
   remove_query_string=true, discarding the fragment entirely.
   Fix: when after_q starts with '#' (empty query + fragment), fall
   through to go_like_reference which encodes and preserves it.
When remove_query_string=true and the URL has both a query and a fragment
(e.g. "?ჸ#ჸ"), the previous fix only handled "?#frag" (empty query).
Extend the fix to any URL starting with '?' that contains a '#' fragment.
Fall through to go_like_reference which strips the query and preserves
the fragment with correct percent-encoding.
Go's url.URL.String() omits an empty trailing fragment (bare '#').
For query-only URL references like '?query#', the previous code returned
the original string including the bare '#', while Go returns '?query'.
For URLs starting with '?' that have a fragment (e.g. '?#ჸ'),
Go's url.URL.String() percent-encodes non-ASCII chars in the fragment
via EscapeFragment. Also, Go omits an empty trailing fragment ('?#' → '?').

Handle these cases early before the 'restore original query' pass
which would otherwise undo the encoding.
The restore-original-query pass was splicing &url[q_start..] which
includes any trailing '#' (empty fragment), overriding the empty-fragment
stripping done by go_like_reference. Now only restores up to '#', and
appends the (already-encoded/stripped) fragment from go_like_reference.
Go's url.Parse decodes %XX sequences where the decoded byte is an
unreserved char (A-Z, a-z, 0-9, -, ., _, ~) as part of path normalization.
E.g. %30 → 0, %41 → A. The url crate preserves them as-is.

Add normalize_pct_encoded_unreserved() and apply it in go_like_reference
on the path portion of all returned values.
Go's url.Parse first splits on '#', then parses the pre-fragment
portion. If that portion starts with ':' (empty scheme), getScheme
returns "missing protocol scheme" and ObfuscateURLString returns '?'.

The Rust code had a check for ':' in the first path segment, but it
was placed after the CTL-in-fragment pre-encode block which returned
early, so inputs like ":#<ctrl>" bypassed the check.

Move the colon check to before the CTL-in-fragment block so it fires
regardless of what the fragment contains.

Fixes parity for input ":#\u{1}" (http_fuzzing_4114246193).
@Eldolfin Eldolfin force-pushed the oscarld/obfuscation-http-parity branch from 694c677 to 43100ec Compare March 6, 2026 16:10
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR aims to reach parity with Go’s URL parsing/serialization behavior for obfuscation by adding Go-specific normalization, encoding, and edge-case handling found via fuzzing.

Changes:

  • Added Go-like URL reference parsing fallback and percent-encoding/normalization helpers.
  • Expanded obfuscation logic to mirror Go’s handling of control chars, fragments, opaque URIs, and query preservation.
  • Added many fuzz/regression tests for parity edge cases.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.


// Try absolute first (like "https://...", "mailto:...", etc.)
if let Ok(abs) = Url::parse(input) {
return abs.to_string();
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

go_like_reference normalizes percent-encoded unreserved chars for relative references via normalize_pct_encoded_unreserved, but absolute URLs return abs.to_string() without that normalization. This likely breaks the stated parity goal for inputs like https://h/%41 (Go emits .../A). Consider applying normalize_pct_encoded_unreserved (path-only) to the absolute serialization as well (while leaving query/fragment untouched per your helper’s semantics).

Suggested change
return abs.to_string();
// For absolute URLs, apply the same Go-like normalization to the path portion
// as we do for relative references. Keep query and fragment unchanged.
let s = abs.to_string();
if let Some(scheme_sep) = s.find("://") {
let after_scheme = scheme_sep + 3;
if let Some(rel_path_pos) = s[after_scheme..].find('/') {
let path_start = after_scheme + rel_path_pos;
let (prefix, path_and_rest) = s.split_at(path_start);
let normalized = normalize_pct_encoded_unreserved(path_and_rest);
return format!("{prefix}{normalized}");
}
}
// Non-hierarchical URLs or ones without a clear path are returned as-is.
return s;

Copilot uses AI. Check for mistakes.
Comment on lines +75 to +87
// Category 1: always encoded (not in validEncoded's explicit allowlist)
'\\' | '^' | '{' | '}' | '|' | '<' | '>' | '`' | ' ' => {
encoded.push('%');
encoded.push_str(&format!("{:02X}", c as u8));
}
// Category 2: encoded only when escape() fallback (handled by caller check)
// These are in Go's validEncoded allowlist but get encoded when escape() is called
'!' | '\'' | '(' | ')' | '*' | '[' | ']' => {
encoded.push('%');
encoded.push_str(&format!("{:02X}", c as u8));
}
_ => encoded.push(c),
}
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Later logic treats '"' as a Category 1 character (has_cat1 includes it), but encode_go_path_chars (and the ASCII-only encoding branch) never encode ". This creates an inconsistent state where needs_full_encoding is triggered by ", but the expected encoding transformation won’t happen. Add '"' to the encoding match arms where Category 1 characters are percent-encoded.

Copilot uses AI. Check for mistakes.
Comment on lines +367 to +395
let raw = if raw.ends_with('#') { raw[..raw.len()-1].to_string() } else { raw };
let result = if raw.is_empty() && !url.is_empty() { url.to_string() } else { raw };
let path_end_for_ascii = url.find(['?', '#']).unwrap_or(url.len());
let has_non_ascii = url[..path_end_for_ascii].bytes().any(|b| b > 127);
let result = if has_non_ascii { encode_go_path_chars(&result) } else {
let qs = result.find('?').unwrap_or(result.len());
let pp = &result[..qs]; let rr = &result[qs..];
let mut enc = String::with_capacity(pp.len()); let mut changed = false;
for c in pp.chars() { match c { '\\' | '^' | '{' | '}' | '|' | '<' | '>' | '`' | ' ' => { enc.push('%'); enc.push_str(&format!("{:02X}", c as u8)); changed = true; } _ => enc.push(c), } }
if changed { if rr.is_empty() { enc } else { format!("{enc}{rr}") } } else { result }
};
// Also encode Cat1 and (when frag has non-ASCII) Cat2 in the result's fragment
let orig_frag_has_non_ascii = url[url.find('#').map(|i|i+1).unwrap_or(url.len())..].bytes().any(|b| b > 127);
let result = if let Some(fs) = result.find('#') {
let (ph, fr) = result.split_at(fs);
let fr_inner = &fr[1..];
let needs = fr_inner.chars().any(|c| matches!(c, '{' | '}' | '|' | '^' | '`' | '\\' | '<' | '>' | ' '))
|| (orig_frag_has_non_ascii && fr_inner.chars().any(|c| matches!(c, '\'' | '[' | ']')));
if needs {
let mut out = ph.to_string(); out.push('#');
for c in fr_inner.chars() {
if matches!(c, '{' | '}' | '|' | '^' | '`' | '\\' | '<' | '>' | ' ')
|| (orig_frag_has_non_ascii && matches!(c, '\'' | '[' | ']'))
{ out.push_str(&format!("%{:02X}", c as u8)); }
else { out.push(c); }
}
out
} else { result }
} else { result };
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This block compresses multiple statements and nested control flow onto single lines, which makes the logic hard to audit and increases the risk of subtle parity regressions. Please reformat this into conventional multi-line Rust and strongly consider extracting this repeated “encode category-1 chars in path-part” logic into a small helper (you already have encode_go_path_chars, and the ASCII-only variant could be another helper).

Suggested change
let raw = if raw.ends_with('#') { raw[..raw.len()-1].to_string() } else { raw };
let result = if raw.is_empty() && !url.is_empty() { url.to_string() } else { raw };
let path_end_for_ascii = url.find(['?', '#']).unwrap_or(url.len());
let has_non_ascii = url[..path_end_for_ascii].bytes().any(|b| b > 127);
let result = if has_non_ascii { encode_go_path_chars(&result) } else {
let qs = result.find('?').unwrap_or(result.len());
let pp = &result[..qs]; let rr = &result[qs..];
let mut enc = String::with_capacity(pp.len()); let mut changed = false;
for c in pp.chars() { match c { '\\' | '^' | '{' | '}' | '|' | '<' | '>' | '`' | ' ' => { enc.push('%'); enc.push_str(&format!("{:02X}", c as u8)); changed = true; } _ => enc.push(c), } }
if changed { if rr.is_empty() { enc } else { format!("{enc}{rr}") } } else { result }
};
// Also encode Cat1 and (when frag has non-ASCII) Cat2 in the result's fragment
let orig_frag_has_non_ascii = url[url.find('#').map(|i|i+1).unwrap_or(url.len())..].bytes().any(|b| b > 127);
let result = if let Some(fs) = result.find('#') {
let (ph, fr) = result.split_at(fs);
let fr_inner = &fr[1..];
let needs = fr_inner.chars().any(|c| matches!(c, '{' | '}' | '|' | '^' | '`' | '\\' | '<' | '>' | ' '))
|| (orig_frag_has_non_ascii && fr_inner.chars().any(|c| matches!(c, '\'' | '[' | ']')));
if needs {
let mut out = ph.to_string(); out.push('#');
for c in fr_inner.chars() {
if matches!(c, '{' | '}' | '|' | '^' | '`' | '\\' | '<' | '>' | ' ')
|| (orig_frag_has_non_ascii && matches!(c, '\'' | '[' | ']'))
{ out.push_str(&format!("%{:02X}", c as u8)); }
else { out.push(c); }
}
out
} else { result }
} else { result };
let raw = if raw.ends_with('#') {
raw[..raw.len() - 1].to_string()
} else {
raw
};
let result = if raw.is_empty() && !url.is_empty() {
url.to_string()
} else {
raw
};
let path_end_for_ascii = url.find(['?', '#']).unwrap_or(url.len());
let has_non_ascii = url[..path_end_for_ascii]
.bytes()
.any(|b| b > 127);
fn encode_ascii_cat1_path_part(input: &str) -> Option<String> {
let qs = input.find('?').unwrap_or(input.len());
let pp = &input[..qs];
let rr = &input[qs..];
let mut enc = String::with_capacity(pp.len());
let mut changed = false;
for c in pp.chars() {
match c {
'\\' | '^' | '{' | '}' | '|' | '<' | '>' | '`' | ' ' => {
enc.push('%');
enc.push_str(&format!("{:02X}", c as u8));
changed = true;
}
_ => enc.push(c),
}
}
if !changed {
return None;
}
if rr.is_empty() {
Some(enc)
} else {
Some(format!("{enc}{rr}"))
}
}
let result = if has_non_ascii {
encode_go_path_chars(&result)
} else {
match encode_ascii_cat1_path_part(&result) {
Some(encoded) => encoded,
None => result,
}
};
// Also encode Cat1 and (when frag has non-ASCII) Cat2 in the result's fragment
let orig_frag_has_non_ascii = url[url
.find('#')
.map(|i| i + 1)
.unwrap_or(url.len())..]
.bytes()
.any(|b| b > 127);
let result = if let Some(fs) = result.find('#') {
let (ph, fr) = result.split_at(fs);
let fr_inner = &fr[1..];
let needs = fr_inner
.chars()
.any(|c| matches!(c, '{' | '}' | '|' | '^' | '`' | '\\' | '<' | '>' | ' '))
|| (orig_frag_has_non_ascii
&& fr_inner
.chars()
.any(|c| matches!(c, '\'' | '[' | ']')));
if needs {
let mut out = ph.to_string();
out.push('#');
for c in fr_inner.chars() {
if matches!(c, '{' | '}' | '|' | '^' | '`' | '\\' | '<' | '>' | ' ')
|| (orig_frag_has_non_ascii
&& matches!(c, '\'' | '[' | ']'))
{
out.push_str(&format!("%{:02X}", c as u8));
} else {
out.push(c);
}
}
out
} else {
result
}
} else {
result
};

Copilot uses AI. Check for mistakes.
Comment on lines +481 to +490
let fixme_url_go_parsing_raw =
go_like_reference(url_for_go_like, remove_query_string);
// Go's url.URL.String() omits a trailing empty fragment (bare '#').
// The url crate keeps it. Strip it here for parity.
let fixme_url_go_parsing = if fixme_url_go_parsing_raw.ends_with('#') {
fixme_url_go_parsing_raw[..fixme_url_go_parsing_raw.len() - 1].to_string()
} else {
fixme_url_go_parsing_raw
};
let result = if fixme_url_go_parsing.is_empty() && !url.is_empty() {
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixme_url_go_parsing_raw / fixme_url_go_parsing read like temporary placeholders but are part of the final implementation. Rename them to something that reflects their meaning (e.g., go_ref_raw / go_ref, resolved_ref_raw / resolved_ref) to reduce cognitive load when debugging parity cases.

Suggested change
let fixme_url_go_parsing_raw =
go_like_reference(url_for_go_like, remove_query_string);
// Go's url.URL.String() omits a trailing empty fragment (bare '#').
// The url crate keeps it. Strip it here for parity.
let fixme_url_go_parsing = if fixme_url_go_parsing_raw.ends_with('#') {
fixme_url_go_parsing_raw[..fixme_url_go_parsing_raw.len() - 1].to_string()
} else {
fixme_url_go_parsing_raw
};
let result = if fixme_url_go_parsing.is_empty() && !url.is_empty() {
let go_ref_raw = go_like_reference(url_for_go_like, remove_query_string);
// Go's url.URL.String() omits a trailing empty fragment (bare '#').
// The url crate keeps it. Strip it here for parity.
let go_ref = if go_ref_raw.ends_with('#') {
go_ref_raw[..go_ref_raw.len() - 1].to_string()
} else {
go_ref_raw
};
let result = if go_ref.is_empty() && !url.is_empty() {

Copilot uses AI. Check for mistakes.
Comment on lines +56 to +68
/// Encode path characters that Go's url.EscapedPath() encodes but the url crate doesn't.
/// Only applied to the path portion (before the first '?').
///
/// Two categories:
/// 1. Always encoded: chars not in Go's validEncoded allowlist (e.g. '\', '^', '{', '}', '|')
/// 2. Encoded only when escape() fallback occurs (non-ASCII present): '!', '\'', '(', ')', '*'
/// These are in validEncoded's allowlist so RawPath is used for pure-ASCII paths.
fn encode_go_path_chars(url_str: &str) -> String {
// Only encode up to the first '?' or '#' — the fragment has different encoding rules
// (e.g., '!' is allowed in fragments per Go's shouldEscape for encodeFragment).
let path_end = url_str
.find(['?', '#'])
.unwrap_or(url_str.len());
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment says “before the first ‘?’”, but the implementation explicitly stops at either ‘?’ or ‘#’. Update the doc to match the actual behavior (or adjust the implementation if the doc is the intended contract).

Copilot uses AI. Check for mistakes.

// Try absolute first (like "https://...", "mailto:...", etc.)
if let Ok(abs) = Url::parse(input) {
return abs.to_string();
Copy link

Copilot AI Mar 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given the new normalize_pct_encoded_unreserved helper and parity goal, it would be valuable to add a regression test that covers absolute URLs with percent-encoded unreserved characters in the path (e.g. https://foo/%41 should round-trip to include A rather than %41 if matching Go). This directly exercises the absolute-return branch here, which currently bypasses path normalization.

Suggested change
return abs.to_string();
// Normalize percent-encoded unreserved characters in the path, to match Go.
return normalize_pct_encoded_unreserved(abs.as_str());

Copilot uses AI. Check for mistakes.
@github-actions
Copy link

github-actions bot commented Mar 6, 2026

🔒 Cargo Deny Results

No issues found!

📦 libdd-trace-obfuscation - ✅ No issues


Updated: 2026-03-06 16:28:53 UTC | Commit: 176984b | dependency-check job results

@pr-commenter
Copy link

pr-commenter bot commented Mar 6, 2026

Benchmarks

Comparison

Benchmark execution time: 2026-03-06 16:34:00

Comparing candidate commit 3d579c4 in PR branch oscarld/obfuscation-http-parity with baseline commit 0f3d46b in branch main.

Found 0 performance improvements and 0 performance regressions! Performance is the same for 57 metrics, 2 unstable metrics.

Explanation

This is an A/B test comparing a candidate commit's performance against that of a baseline commit. Performance changes are noted in the tables below as:

  • 🟩 = significantly better candidate vs. baseline
  • 🟥 = significantly worse candidate vs. baseline

We compute a confidence interval (CI) over the relative difference of means between metrics from the candidate and baseline commits, considering the baseline as the reference.

If the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD), the change is considered significant.

Feel free to reach out to #apm-benchmarking-platform on Slack if you have any questions.

More details about the CI and significant changes

You can imagine this CI as a range of values that is likely to contain the true difference of means between the candidate and baseline commits.

CIs of the difference of means are often centered around 0%, because often changes are not that big:

---------------------------------(------|---^--------)-------------------------------->
                              -0.6%    0%  0.3%     +1.2%
                                 |          |        |
         lower bound of the CI --'          |        |
sample mean (center of the CI) -------------'        |
         upper bound of the CI ----------------------'

As described above, a change is considered significant if the CI is entirely outside the configured SIGNIFICANT_IMPACT_THRESHOLD (or the deprecated UNCONFIDENCE_THRESHOLD).

For instance, for an execution time metric, this confidence interval indicates a significantly worse performance:

----------------------------------------|---------|---(---------^---------)---------->
                                       0%        1%  1.3%      2.2%      3.1%
                                                  |   |         |         |
       significant impact threshold --------------'   |         |         |
                      lower bound of CI --------------'         |         |
       sample mean (center of the CI) --------------------------'         |
                      upper bound of CI ----------------------------------'

Candidate

Candidate benchmark details

Group 1

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
sql/obfuscate_sql_string execution_time 87.927µs 88.200µs ± 0.206µs 88.172µs ± 0.042µs 88.223µs 88.354µs 88.906µs 90.432µs 2.56% 7.770 74.912 0.23% 0.015µs 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
sql/obfuscate_sql_string execution_time [88.172µs; 88.229µs] or [-0.032%; +0.032%] None None None

Group 2

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
concentrator/add_spans_to_concentrator execution_time 10.617ms 10.646ms ± 0.015ms 10.644ms ± 0.008ms 10.653ms 10.666ms 10.699ms 10.751ms 1.00% 2.387 12.518 0.14% 0.001ms 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
concentrator/add_spans_to_concentrator execution_time [10.644ms; 10.648ms] or [-0.020%; +0.020%] None None None

Group 3

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
ip_address/quantize_peer_ip_address_benchmark execution_time 4.948µs 5.029µs ± 0.047µs 5.039µs ± 0.033µs 5.053µs 5.115µs 5.117µs 5.118µs 1.57% 0.224 -0.935 0.93% 0.003µs 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
ip_address/quantize_peer_ip_address_benchmark execution_time [5.022µs; 5.035µs] or [-0.129%; +0.129%] None None None

Group 4

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
tags/replace_trace_tags execution_time 2.446µs 2.468µs ± 0.018µs 2.462µs ± 0.008µs 2.475µs 2.514µs 2.524µs 2.534µs 2.93% 1.661 2.503 0.73% 0.001µs 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
tags/replace_trace_tags execution_time [2.466µs; 2.471µs] or [-0.102%; +0.102%] None None None

Group 5

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
receiver_entry_point/report/2597 execution_time 3.130ms 3.164ms ± 0.033ms 3.153ms ± 0.010ms 3.169ms 3.219ms 3.255ms 3.407ms 8.05% 2.960 14.692 1.04% 0.002ms 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
receiver_entry_point/report/2597 execution_time [3.159ms; 3.168ms] or [-0.144%; +0.144%] None None None

Group 6

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
redis/obfuscate_redis_string execution_time 33.053µs 33.712µs ± 0.869µs 33.181µs ± 0.072µs 34.934µs 35.022µs 35.220µs 36.742µs 10.73% 1.016 -0.569 2.57% 0.061µs 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
redis/obfuscate_redis_string execution_time [33.591µs; 33.832µs] or [-0.357%; +0.357%] None None None

Group 7

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
single_flag_killswitch/rules-based execution_time 188.102ns 190.535ns ± 2.150ns 190.140ns ± 1.442ns 191.618ns 194.823ns 196.562ns 198.920ns 4.62% 1.082 0.889 1.13% 0.152ns 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
single_flag_killswitch/rules-based execution_time [190.237ns; 190.833ns] or [-0.156%; +0.156%] None None None

Group 8

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
write only interface execution_time 1.274µs 3.199µs ± 1.400µs 2.989µs ± 0.032µs 3.022µs 3.649µs 13.816µs 14.886µs 398.08% 7.357 55.377 43.65% 0.099µs 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
write only interface execution_time [3.005µs; 3.393µs] or [-6.065%; +6.065%] None None None

Group 9

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
benching deserializing traces from msgpack to their internal representation execution_time 48.094ms 48.363ms ± 1.401ms 48.188ms ± 0.038ms 48.231ms 48.300ms 53.205ms 64.045ms 32.91% 9.454 93.508 2.89% 0.099ms 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
benching deserializing traces from msgpack to their internal representation execution_time [48.169ms; 48.557ms] or [-0.402%; +0.402%] None None None

Group 10

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
credit_card/is_card_number/ execution_time 3.891µs 3.913µs ± 0.003µs 3.913µs ± 0.002µs 3.915µs 3.918µs 3.920µs 3.921µs 0.20% -1.274 8.656 0.08% 0.000µs 1 200
credit_card/is_card_number/ throughput 255045352.871op/s 255552141.007op/s ± 214316.044op/s 255551253.599op/s ± 136153.151op/s 255689146.021op/s 255848565.855op/s 255926362.274op/s 256991325.336op/s 0.56% 1.297 8.826 0.08% 15154.433op/s 1 200
credit_card/is_card_number/ 3782-8224-6310-005 execution_time 79.037µs 79.785µs ± 0.403µs 79.759µs ± 0.313µs 80.097µs 80.452µs 80.722µs 81.003µs 1.56% 0.304 -0.554 0.50% 0.028µs 1 200
credit_card/is_card_number/ 3782-8224-6310-005 throughput 12345178.238op/s 12534064.043op/s ± 63139.685op/s 12537744.501op/s ± 49356.239op/s 12586061.881op/s 12631034.516op/s 12647314.258op/s 12652346.553op/s 0.91% -0.284 -0.580 0.50% 4464.650op/s 1 200
credit_card/is_card_number/ 378282246310005 execution_time 72.408µs 73.090µs ± 0.415µs 73.055µs ± 0.267µs 73.312µs 73.848µs 74.327µs 74.523µs 2.01% 0.816 0.615 0.57% 0.029µs 1 200
credit_card/is_card_number/ 378282246310005 throughput 13418685.314op/s 13682129.732op/s ± 77286.912op/s 13688390.209op/s ± 50132.652op/s 13741546.213op/s 13783437.888op/s 13806066.098op/s 13810684.349op/s 0.89% -0.783 0.532 0.56% 5465.010op/s 1 200
credit_card/is_card_number/37828224631 execution_time 3.892µs 3.913µs ± 0.007µs 3.912µs ± 0.002µs 3.914µs 3.917µs 3.922µs 3.986µs 1.88% 7.806 71.537 0.19% 0.001µs 1 200
credit_card/is_card_number/37828224631 throughput 250888145.015op/s 255557791.495op/s ± 473039.459op/s 255613879.842op/s ± 103963.682op/s 255712922.842op/s 255802529.094op/s 255850308.106op/s 256934001.335op/s 0.52% -7.735 70.726 0.18% 33448.941op/s 1 200
credit_card/is_card_number/378282246310005 execution_time 69.230µs 69.836µs ± 0.377µs 69.799µs ± 0.258µs 70.072µs 70.472µs 70.851µs 71.672µs 2.68% 1.030 2.249 0.54% 0.027µs 1 200
credit_card/is_card_number/378282246310005 throughput 13952382.457op/s 14319618.111op/s ± 76826.928op/s 14326773.736op/s ± 52875.393op/s 14376431.311op/s 14423148.505op/s 14440067.172op/s 14444707.815op/s 0.82% -0.979 2.019 0.54% 5432.484op/s 1 200
credit_card/is_card_number/37828224631000521389798 execution_time 52.145µs 52.233µs ± 0.111µs 52.220µs ± 0.026µs 52.249µs 52.291µs 52.365µs 53.347µs 2.16% 8.284 75.847 0.21% 0.008µs 1 200
credit_card/is_card_number/37828224631000521389798 throughput 18745077.587op/s 19144994.723op/s ± 40030.253op/s 19149833.182op/s ± 9698.889op/s 19159226.293op/s 19167621.384op/s 19173485.524op/s 19177401.350op/s 0.14% -8.225 75.073 0.21% 2830.566op/s 1 200
credit_card/is_card_number/x371413321323331 execution_time 6.027µs 6.038µs ± 0.012µs 6.034µs ± 0.002µs 6.037µs 6.071µs 6.077µs 6.081µs 0.77% 2.377 4.396 0.20% 0.001µs 1 200
credit_card/is_card_number/x371413321323331 throughput 164459678.000op/s 165616144.905op/s ± 322508.053op/s 165723383.575op/s ± 61069.424op/s 165776331.712op/s 165838519.112op/s 165874555.937op/s 165915225.230op/s 0.12% -2.373 4.378 0.19% 22804.763op/s 1 200
credit_card/is_card_number_no_luhn/ execution_time 3.893µs 3.912µs ± 0.002µs 3.912µs ± 0.001µs 3.913µs 3.915µs 3.917µs 3.921µs 0.25% -1.907 19.052 0.06% 0.000µs 1 200
credit_card/is_card_number_no_luhn/ throughput 255012918.693op/s 255636324.563op/s ± 153509.269op/s 255651451.790op/s ± 89383.724op/s 255725764.371op/s 255808163.368op/s 255859714.287op/s 256853064.778op/s 0.47% 1.939 19.305 0.06% 10854.745op/s 1 200
credit_card/is_card_number_no_luhn/ 3782-8224-6310-005 execution_time 64.130µs 64.418µs ± 0.144µs 64.408µs ± 0.095µs 64.512µs 64.671µs 64.824µs 64.890µs 0.75% 0.563 0.385 0.22% 0.010µs 1 200
credit_card/is_card_number_no_luhn/ 3782-8224-6310-005 throughput 15410748.305op/s 15523588.929op/s ± 34719.691op/s 15526115.873op/s ± 22921.179op/s 15547738.225op/s 15573463.295op/s 15589125.587op/s 15593266.827op/s 0.43% -0.549 0.358 0.22% 2455.053op/s 1 200
credit_card/is_card_number_no_luhn/ 378282246310005 execution_time 57.801µs 58.044µs ± 0.161µs 58.001µs ± 0.093µs 58.123µs 58.362µs 58.508µs 58.668µs 1.15% 1.124 1.139 0.28% 0.011µs 1 200
credit_card/is_card_number_no_luhn/ 378282246310005 throughput 17044996.624op/s 17228469.838op/s ± 47597.138op/s 17241058.031op/s ± 27700.204op/s 17263346.032op/s 17285386.934op/s 17296191.812op/s 17300596.871op/s 0.35% -1.108 1.088 0.28% 3365.626op/s 1 200
credit_card/is_card_number_no_luhn/37828224631 execution_time 3.894µs 3.912µs ± 0.003µs 3.911µs ± 0.001µs 3.913µs 3.916µs 3.919µs 3.920µs 0.22% -0.732 10.043 0.07% 0.000µs 1 200
credit_card/is_card_number_no_luhn/37828224631 throughput 255108098.191op/s 255652538.807op/s ± 175124.761op/s 255675889.764op/s ± 88288.709op/s 255754392.566op/s 255852202.646op/s 255900363.081op/s 256836604.710op/s 0.45% 0.756 10.192 0.07% 12383.191op/s 1 200
credit_card/is_card_number_no_luhn/378282246310005 execution_time 54.580µs 54.993µs ± 0.295µs 54.924µs ± 0.192µs 55.148µs 55.531µs 55.829µs 55.888µs 1.76% 0.857 0.063 0.54% 0.021µs 1 200
credit_card/is_card_number_no_luhn/378282246310005 throughput 17892824.928op/s 18184639.982op/s ± 97157.407op/s 18206870.363op/s ± 63729.309op/s 18265658.199op/s 18300785.080op/s 18314443.478op/s 18321847.615op/s 0.63% -0.835 0.011 0.53% 6870.066op/s 1 200
credit_card/is_card_number_no_luhn/37828224631000521389798 execution_time 52.146µs 52.218µs ± 0.034µs 52.213µs ± 0.019µs 52.237µs 52.268µs 52.318µs 52.450µs 0.45% 1.944 9.699 0.07% 0.002µs 1 200
credit_card/is_card_number_no_luhn/37828224631000521389798 throughput 19065875.380op/s 19150613.849op/s ± 12600.380op/s 19152330.815op/s ± 7091.941op/s 19158476.878op/s 19166891.686op/s 19171998.817op/s 19176956.969op/s 0.13% -1.929 9.575 0.07% 890.981op/s 1 200
credit_card/is_card_number_no_luhn/x371413321323331 execution_time 6.029µs 6.036µs ± 0.009µs 6.035µs ± 0.003µs 6.037µs 6.043µs 6.073µs 6.114µs 1.31% 5.384 37.873 0.14% 0.001µs 1 200
credit_card/is_card_number_no_luhn/x371413321323331 throughput 163555751.388op/s 165669283.806op/s ± 235324.505op/s 165704747.442op/s ± 69365.556op/s 165777712.449op/s 165830480.397op/s 165852260.019op/s 165868306.571op/s 0.10% -5.338 37.255 0.14% 16639.955op/s 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
credit_card/is_card_number/ execution_time [3.913µs; 3.914µs] or [-0.012%; +0.012%] None None None
credit_card/is_card_number/ throughput [255522438.864op/s; 255581843.149op/s] or [-0.012%; +0.012%] None None None
credit_card/is_card_number/ 3782-8224-6310-005 execution_time [79.729µs; 79.840µs] or [-0.070%; +0.070%] None None None
credit_card/is_card_number/ 3782-8224-6310-005 throughput [12525313.490op/s; 12542814.596op/s] or [-0.070%; +0.070%] None None None
credit_card/is_card_number/ 378282246310005 execution_time [73.033µs; 73.148µs] or [-0.079%; +0.079%] None None None
credit_card/is_card_number/ 378282246310005 throughput [13671418.510op/s; 13692840.955op/s] or [-0.078%; +0.078%] None None None
credit_card/is_card_number/37828224631 execution_time [3.912µs; 3.914µs] or [-0.026%; +0.026%] None None None
credit_card/is_card_number/37828224631 throughput [255492232.775op/s; 255623350.214op/s] or [-0.026%; +0.026%] None None None
credit_card/is_card_number/378282246310005 execution_time [69.784µs; 69.888µs] or [-0.075%; +0.075%] None None None
credit_card/is_card_number/378282246310005 throughput [14308970.638op/s; 14330265.584op/s] or [-0.074%; +0.074%] None None None
credit_card/is_card_number/37828224631000521389798 execution_time [52.218µs; 52.249µs] or [-0.029%; +0.029%] None None None
credit_card/is_card_number/37828224631000521389798 throughput [19139446.915op/s; 19150542.531op/s] or [-0.029%; +0.029%] None None None
credit_card/is_card_number/x371413321323331 execution_time [6.036µs; 6.040µs] or [-0.027%; +0.027%] None None None
credit_card/is_card_number/x371413321323331 throughput [165571448.391op/s; 165660841.420op/s] or [-0.027%; +0.027%] None None None
credit_card/is_card_number_no_luhn/ execution_time [3.911µs; 3.912µs] or [-0.008%; +0.008%] None None None
credit_card/is_card_number_no_luhn/ throughput [255615049.655op/s; 255657599.471op/s] or [-0.008%; +0.008%] None None None
credit_card/is_card_number_no_luhn/ 3782-8224-6310-005 execution_time [64.398µs; 64.438µs] or [-0.031%; +0.031%] None None None
credit_card/is_card_number_no_luhn/ 3782-8224-6310-005 throughput [15518777.113op/s; 15528400.744op/s] or [-0.031%; +0.031%] None None None
credit_card/is_card_number_no_luhn/ 378282246310005 execution_time [58.022µs; 58.066µs] or [-0.038%; +0.038%] None None None
credit_card/is_card_number_no_luhn/ 378282246310005 throughput [17221873.333op/s; 17235066.344op/s] or [-0.038%; +0.038%] None None None
credit_card/is_card_number_no_luhn/37828224631 execution_time [3.911µs; 3.912µs] or [-0.009%; +0.009%] None None None
credit_card/is_card_number_no_luhn/37828224631 throughput [255628268.200op/s; 255676809.415op/s] or [-0.009%; +0.009%] None None None
credit_card/is_card_number_no_luhn/378282246310005 execution_time [54.952µs; 55.034µs] or [-0.074%; +0.074%] None None None
credit_card/is_card_number_no_luhn/378282246310005 throughput [18171174.900op/s; 18198105.064op/s] or [-0.074%; +0.074%] None None None
credit_card/is_card_number_no_luhn/37828224631000521389798 execution_time [52.213µs; 52.222µs] or [-0.009%; +0.009%] None None None
credit_card/is_card_number_no_luhn/37828224631000521389798 throughput [19148867.558op/s; 19152360.141op/s] or [-0.009%; +0.009%] None None None
credit_card/is_card_number_no_luhn/x371413321323331 execution_time [6.035µs; 6.037µs] or [-0.020%; +0.020%] None None None
credit_card/is_card_number_no_luhn/x371413321323331 throughput [165636670.093op/s; 165701897.519op/s] or [-0.020%; +0.020%] None None None

Group 11

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
sdk_test_data/rules-based execution_time 145.525µs 147.592µs ± 1.711µs 147.355µs ± 0.568µs 147.967µs 149.006µs 154.085µs 164.373µs 11.55% 5.924 49.658 1.16% 0.121µs 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
sdk_test_data/rules-based execution_time [147.355µs; 147.829µs] or [-0.161%; +0.161%] None None None

Group 12

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
two way interface execution_time 18.208µs 27.147µs ± 10.060µs 18.679µs ± 0.182µs 35.686µs 42.672µs 48.307µs 73.998µs 296.15% 0.882 0.868 36.96% 0.711µs 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
two way interface execution_time [25.753µs; 28.542µs] or [-5.136%; +5.136%] None None None

Group 13

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
profile_add_sample2_frames_x1000 execution_time 746.704µs 747.858µs ± 0.560µs 747.848µs ± 0.396µs 748.232µs 748.798µs 749.125µs 749.550µs 0.23% 0.278 -0.198 0.07% 0.040µs 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
profile_add_sample2_frames_x1000 execution_time [747.780µs; 747.935µs] or [-0.010%; +0.010%] None None None

Group 14

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
normalization/normalize_name/normalize_name/Too-Long-.Too-Long-.Too-Long-.Too-Long-.Too-Long-.Too-Lo... execution_time 185.336µs 185.748µs ± 0.221µs 185.752µs ± 0.192µs 185.921µs 186.120µs 186.201µs 186.317µs 0.30% 0.173 -0.876 0.12% 0.016µs 1 200
normalization/normalize_name/normalize_name/Too-Long-.Too-Long-.Too-Long-.Too-Long-.Too-Long-.Too-Lo... throughput 5367191.726op/s 5383656.794op/s ± 6406.856op/s 5383533.346op/s ± 5557.435op/s 5389475.137op/s 5392998.667op/s 5395006.916op/s 5395608.002op/s 0.22% -0.169 -0.879 0.12% 453.033op/s 1 200
normalization/normalize_name/normalize_name/bad-name execution_time 18.012µs 18.101µs ± 0.034µs 18.099µs ± 0.020µs 18.120µs 18.156µs 18.179µs 18.308µs 1.16% 1.067 6.033 0.19% 0.002µs 1 200
normalization/normalize_name/normalize_name/bad-name throughput 54619702.290op/s 55246900.508op/s ± 104001.734op/s 55252210.088op/s ± 60145.131op/s 55296851.742op/s 55400122.262op/s 55470673.042op/s 55518569.095op/s 0.48% -1.028 5.801 0.19% 7354.033op/s 1 200
normalization/normalize_name/normalize_name/good execution_time 10.303µs 10.432µs ± 0.061µs 10.433µs ± 0.042µs 10.469µs 10.539µs 10.560µs 10.635µs 1.94% 0.165 -0.210 0.59% 0.004µs 1 200
normalization/normalize_name/normalize_name/good throughput 94025197.328op/s 95865681.009op/s ± 563183.261op/s 95849255.293op/s ± 385449.448op/s 96284757.847op/s 96750179.826op/s 96985469.810op/s 97063319.845op/s 1.27% -0.134 -0.248 0.59% 39823.070op/s 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
normalization/normalize_name/normalize_name/Too-Long-.Too-Long-.Too-Long-.Too-Long-.Too-Long-.Too-Lo... execution_time [185.717µs; 185.778µs] or [-0.016%; +0.016%] None None None
normalization/normalize_name/normalize_name/Too-Long-.Too-Long-.Too-Long-.Too-Long-.Too-Long-.Too-Lo... throughput [5382768.865op/s; 5384544.722op/s] or [-0.016%; +0.016%] None None None
normalization/normalize_name/normalize_name/bad-name execution_time [18.096µs; 18.105µs] or [-0.026%; +0.026%] None None None
normalization/normalize_name/normalize_name/bad-name throughput [55232486.868op/s; 55261314.148op/s] or [-0.026%; +0.026%] None None None
normalization/normalize_name/normalize_name/good execution_time [10.423µs; 10.440µs] or [-0.081%; +0.081%] None None None
normalization/normalize_name/normalize_name/good throughput [95787629.226op/s; 95943732.793op/s] or [-0.081%; +0.081%] None None None

Group 15

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
normalization/normalize_trace/test_trace execution_time 240.294ns 249.101ns ± 13.548ns 243.423ns ± 2.320ns 250.246ns 285.406ns 296.374ns 296.976ns 22.00% 2.301 4.303 5.43% 0.958ns 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
normalization/normalize_trace/test_trace execution_time [247.223ns; 250.978ns] or [-0.754%; +0.754%] None None None

Group 16

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
benching string interning on wordpress profile execution_time 159.565µs 160.789µs ± 0.252µs 160.771µs ± 0.128µs 160.945µs 161.228µs 161.437µs 161.807µs 0.64% 0.149 3.466 0.16% 0.018µs 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
benching string interning on wordpress profile execution_time [160.754µs; 160.824µs] or [-0.022%; +0.022%] None None None

Group 17

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
normalization/normalize_service/normalize_service/A0000000000000000000000000000000000000000000000000... execution_time 533.938µs 534.675µs ± 0.488µs 534.644µs ± 0.222µs 534.827µs 535.244µs 535.600µs 539.626µs 0.93% 5.357 51.299 0.09% 0.034µs 1 200
normalization/normalize_service/normalize_service/A0000000000000000000000000000000000000000000000000... throughput 1853134.108op/s 1870296.352op/s ± 1698.235op/s 1870404.924op/s ± 776.850op/s 1871276.281op/s 1872092.774op/s 1872615.507op/s 1872875.182op/s 0.13% -5.290 50.398 0.09% 120.083op/s 1 200
normalization/normalize_service/normalize_service/Data🐨dog🐶 繋がっ⛰てて execution_time 381.223µs 381.845µs ± 0.280µs 381.840µs ± 0.175µs 382.017µs 382.299µs 382.483µs 382.789µs 0.25% 0.169 0.186 0.07% 0.020µs 1 200
normalization/normalize_service/normalize_service/Data🐨dog🐶 繋がっ⛰てて throughput 2612403.191op/s 2618864.843op/s ± 1919.156op/s 2618901.248op/s ± 1201.053op/s 2620059.977op/s 2622123.642op/s 2623106.227op/s 2623133.598op/s 0.16% -0.164 0.181 0.07% 135.705op/s 1 200
normalization/normalize_service/normalize_service/Test Conversion 0f Weird !@#$%^&**() Characters execution_time 189.837µs 190.319µs ± 0.227µs 190.298µs ± 0.149µs 190.460µs 190.715µs 190.893µs 191.298µs 0.53% 0.636 0.901 0.12% 0.016µs 1 200
normalization/normalize_service/normalize_service/Test Conversion 0f Weird !@#$%^&**() Characters throughput 5227442.085op/s 5254344.238op/s ± 6270.880op/s 5254924.603op/s ± 4117.399op/s 5258787.048op/s 5263950.985op/s 5265406.362op/s 5267665.908op/s 0.24% -0.627 0.873 0.12% 443.418op/s 1 200
normalization/normalize_service/normalize_service/[empty string] execution_time 37.332µs 37.501µs ± 0.063µs 37.497µs ± 0.040µs 37.539µs 37.604µs 37.654µs 37.684µs 0.50% 0.231 0.030 0.17% 0.004µs 1 200
normalization/normalize_service/normalize_service/[empty string] throughput 26536286.094op/s 26666233.841op/s ± 44874.689op/s 26668541.355op/s ± 28329.109op/s 26696737.619op/s 26736492.070op/s 26759779.604op/s 26786760.113op/s 0.44% -0.221 0.025 0.17% 3173.120op/s 1 200
normalization/normalize_service/normalize_service/test_ASCII execution_time 45.813µs 45.914µs ± 0.142µs 45.892µs ± 0.029µs 45.935µs 46.000µs 46.025µs 47.800µs 4.16% 11.746 153.126 0.31% 0.010µs 1 200
normalization/normalize_service/normalize_service/test_ASCII throughput 20920474.156op/s 21780014.641op/s ± 65125.914op/s 21790222.056op/s ± 13769.621op/s 21801018.050op/s 21812634.828op/s 21822588.564op/s 21827657.639op/s 0.17% -11.586 150.276 0.30% 4605.098op/s 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
normalization/normalize_service/normalize_service/A0000000000000000000000000000000000000000000000000... execution_time [534.607µs; 534.743µs] or [-0.013%; +0.013%] None None None
normalization/normalize_service/normalize_service/A0000000000000000000000000000000000000000000000000... throughput [1870060.993op/s; 1870531.711op/s] or [-0.013%; +0.013%] None None None
normalization/normalize_service/normalize_service/Data🐨dog🐶 繋がっ⛰てて execution_time [381.806µs; 381.884µs] or [-0.010%; +0.010%] None None None
normalization/normalize_service/normalize_service/Data🐨dog🐶 繋がっ⛰てて throughput [2618598.867op/s; 2619130.820op/s] or [-0.010%; +0.010%] None None None
normalization/normalize_service/normalize_service/Test Conversion 0f Weird !@#$%^&**() Characters execution_time [190.287µs; 190.350µs] or [-0.017%; +0.017%] None None None
normalization/normalize_service/normalize_service/Test Conversion 0f Weird !@#$%^&**() Characters throughput [5253475.155op/s; 5255213.322op/s] or [-0.017%; +0.017%] None None None
normalization/normalize_service/normalize_service/[empty string] execution_time [37.492µs; 37.509µs] or [-0.023%; +0.023%] None None None
normalization/normalize_service/normalize_service/[empty string] throughput [26660014.641op/s; 26672453.041op/s] or [-0.023%; +0.023%] None None None
normalization/normalize_service/normalize_service/test_ASCII execution_time [45.894µs; 45.934µs] or [-0.043%; +0.043%] None None None
normalization/normalize_service/normalize_service/test_ASCII throughput [21770988.816op/s; 21789040.467op/s] or [-0.041%; +0.041%] None None None

Group 18

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
profile_add_sample_frames_x1000 execution_time 4.187ms 4.193ms ± 0.009ms 4.191ms ± 0.001ms 4.193ms 4.207ms 4.212ms 4.281ms 2.16% 5.834 51.820 0.21% 0.001ms 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
profile_add_sample_frames_x1000 execution_time [4.192ms; 4.195ms] or [-0.029%; +0.029%] None None None

Group 19

cpu_model git_commit_sha git_commit_date git_branch
Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz 3d579c4 1772813801 oscarld/obfuscation-http-parity
scenario metric min mean ± sd median ± mad p75 p95 p99 max peak_to_median_ratio skewness kurtosis cv sem runs sample_size
benching serializing traces from their internal representation to msgpack execution_time 13.940ms 13.991ms ± 0.032ms 13.987ms ± 0.015ms 14.001ms 14.039ms 14.107ms 14.184ms 1.41% 2.258 8.479 0.23% 0.002ms 1 200
scenario metric 95% CI mean Shapiro-Wilk pvalue Ljung-Box pvalue (lag=1) Dip test pvalue
benching serializing traces from their internal representation to msgpack execution_time [13.987ms; 13.995ms] or [-0.032%; +0.032%] None None None

Baseline

Omitted due to size.

@dd-octo-sts
Copy link
Contributor

dd-octo-sts bot commented Mar 6, 2026

Artifact Size Benchmark Report

aarch64-alpine-linux-musl
Artifact Baseline Commit Change
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.a 98.65 MB 98.65 MB 0% (0 B) 👌
/aarch64-alpine-linux-musl/lib/libdatadog_profiling.so 8.70 MB 8.70 MB 0% (0 B) 👌
aarch64-unknown-linux-gnu
Artifact Baseline Commit Change
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.a 114.29 MB 114.29 MB 0% (0 B) 👌
/aarch64-unknown-linux-gnu/lib/libdatadog_profiling.so 11.29 MB 11.29 MB 0% (0 B) 👌
libdatadog-x64-windows
Artifact Baseline Commit Change
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.dll 27.16 MB 27.16 MB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.lib 76.26 KB 76.26 KB 0% (0 B) 👌
/libdatadog-x64-windows/debug/dynamic/datadog_profiling_ffi.pdb 186.04 MB 186.02 MB -0% (-16.00 KB) 👌
/libdatadog-x64-windows/debug/static/datadog_profiling_ffi.lib 917.19 MB 917.19 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.dll 9.93 MB 9.93 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.lib 76.26 KB 76.26 KB 0% (0 B) 👌
/libdatadog-x64-windows/release/dynamic/datadog_profiling_ffi.pdb 24.76 MB 24.76 MB 0% (0 B) 👌
/libdatadog-x64-windows/release/static/datadog_profiling_ffi.lib 51.43 MB 51.43 MB 0% (0 B) 👌
libdatadog-x86-windows
Artifact Baseline Commit Change
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.dll 22.97 MB 22.97 MB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.lib 77.44 KB 77.44 KB 0% (0 B) 👌
/libdatadog-x86-windows/debug/dynamic/datadog_profiling_ffi.pdb 190.23 MB 190.23 MB 0% (0 B) 👌
/libdatadog-x86-windows/debug/static/datadog_profiling_ffi.lib 900.84 MB 900.84 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.dll 7.53 MB 7.53 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.lib 77.44 KB 77.44 KB 0% (0 B) 👌
/libdatadog-x86-windows/release/dynamic/datadog_profiling_ffi.pdb 26.52 MB 26.52 MB 0% (0 B) 👌
/libdatadog-x86-windows/release/static/datadog_profiling_ffi.lib 47.06 MB 47.06 MB 0% (0 B) 👌
x86_64-alpine-linux-musl
Artifact Baseline Commit Change
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.a 86.54 MB 86.54 MB 0% (0 B) 👌
/x86_64-alpine-linux-musl/lib/libdatadog_profiling.so 10.23 MB 10.23 MB 0% (0 B) 👌
x86_64-unknown-linux-gnu
Artifact Baseline Commit Change
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.a 107.16 MB 107.16 MB 0% (0 B) 👌
/x86_64-unknown-linux-gnu/lib/libdatadog_profiling.so 11.98 MB 11.98 MB 0% (0 B) 👌

@codecov-commenter
Copy link

Codecov Report

❌ Patch coverage is 66.20370% with 146 lines in your changes missing coverage. Please review.
✅ Project coverage is 71.15%. Comparing base (0f3d46b) to head (3d579c4).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1684      +/-   ##
==========================================
- Coverage   71.19%   71.15%   -0.04%     
==========================================
  Files         427      427              
  Lines       62852    63282     +430     
==========================================
+ Hits        44746    45031     +285     
- Misses      18106    18251     +145     
Components Coverage Δ
libdd-crashtracker 63.01% <ø> (+0.08%) ⬆️
libdd-crashtracker-ffi 16.56% <ø> (ø)
libdd-alloc 98.77% <ø> (ø)
libdd-data-pipeline 88.02% <ø> (ø)
libdd-data-pipeline-ffi 75.72% <ø> (ø)
libdd-common 79.73% <ø> (ø)
libdd-common-ffi 73.40% <ø> (ø)
libdd-telemetry 62.48% <ø> (ø)
libdd-telemetry-ffi 16.75% <ø> (ø)
libdd-dogstatsd-client 82.64% <ø> (ø)
datadog-ipc 80.74% <ø> (-0.12%) ⬇️
libdd-profiling 81.60% <ø> (ø)
libdd-profiling-ffi 63.65% <ø> (ø)
datadog-sidecar 32.00% <ø> (ø)
datdog-sidecar-ffi 6.43% <ø> (ø)
spawn-worker 54.69% <ø> (ø)
libdd-tinybytes 93.16% <ø> (ø)
libdd-trace-normalization 81.71% <ø> (ø)
libdd-trace-obfuscation 87.40% <66.20%> (-7.30%) ⬇️
libdd-trace-protobuf 68.00% <ø> (ø)
libdd-trace-utils 89.07% <ø> (ø)
datadog-tracer-flare 88.95% <ø> (ø)
libdd-log 74.69% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants