Skip to content

Stop re-validating addresses the decoder just generated - #119

Merged
EONRaider merged 1 commit into
masterfrom
claude/decoder-depth-polish-8m54to
Sep 1, 2026
Merged

Stop re-validating addresses the decoder just generated#119
EONRaider merged 1 commit into
masterfrom
claude/decoder-depth-polish-8m54to

Conversation

@EONRaider

Copy link
Copy Markdown
Owner

Summary

On the decode path a MAC rendered from six raw bytes by bytes_to_mac() was immediately matched against mac_regex to prove what the conversion already guaranteed — and the same for validate_ipv4_addr() against inet_ntop() output. With #82 and #83 merged, re.Pattern.match was the largest single remaining cost in a corpus profile.

Ethernet, ARP and IPv4 now build their decoded instance directly (object.__new__ + object.__setattr__ per field), skipping __init__/__post_init__on that path only.

Strictness is unchanged — that was the thing to get right

The issue is explicit that construction-time strictness is a deliberate differentiator, so the invariant is pinned from both sides:

  • Ethernet(dst="nonsense", ...) still raises InvalidMACAddressError; same for ARP and IPv4. Tested.
  • Ethernet.decode(...) runs no regex at all — tested by patching the compiled patterns with a spy whose .match raises, so any re-validation fails the test loudly rather than silently costing time.
  • Decoded instances are compared against constructed ones (equality touches every field), so a field the bypass forgot to set surfaces as an AttributeError in the suite instead of lurking.

The __post_init__ checks the bypass skips are ones decode() already establishes — IPv4's IHL is 4 bits and was rejected below 5, and the options are sliced to exactly ihl * 4 bytes after the buffer was confirmed to hold them. That reasoning is recorded at each site and in a new "Decode-path construction" section in _base.py.

Scope was kept deliberately narrow: the other protocols' __post_init__ are cheap integer comparisons, so bypassing them would buy little and risk more.

Measured

200k calls, best of run, CPython 3.12 on this machine:

beforeafter
Ethernet.decode2330 ns830 ns2.8×
IPv4.decode4936 ns2596 ns1.9×
corpus walk (97 frames)76,500 f/s113,200 f/s1.48×

That is ~3.8 µs saved per Ethernet+IPv4 frame, against the ~2.3 µs the issue attributes to the regexes alone — skipping the whole constructor path rather than only the validators accounts for the difference. Cumulative over #82 + #84: 58,300 → 113,200 f/s (1.94×).

Of the two options the issue weighed, this is the first (object.__new__); a central helper taking **fields measured 855 ns against 393 ns inline, because the per-field loop dominates, so the shortcut is written out at the three sites instead.

Verification

  • uv run ruff check and uv run ruff format --check are clean
  • uv run mypy is clean (strict)
  • uv run pytest — 753 passed
  • CHANGELOG.md entry under ## [Unreleased]

Closes#84.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QJnVMNGwTRDktC4rkABtgt


Generated by Claude Code

Every header's __post_init__ runs the address validators, including
when the instance came from decode(). On the decode path that means a
MAC rendered from six raw bytes by bytes_to_mac() is immediately
matched against mac_regex to prove what the conversion already
guarantees; likewise validate_ipv4_addr() against inet_ntop output.
After the dispatch and MAC-rendering work landed, re.Pattern.match was
the largest single remaining cost in a corpus profile.
Ethernet, ARP and IPv4 — the three headers carrying addresses — now
build their decoded instance directly with object.__new__ plus
object.__setattr__, skipping __init__ and __post_init__ on that path.
The other protocols are left alone: their __post_init__ checks are
cheap integer comparisons, so bypassing them would buy little and risk
more.
Strictness on construction is untouched and remains the differentiator:
Ethernet(dst="nonsense", ...) still raises InvalidMACAddressError, and
that is now asserted next to a test that patches the compiled patterns
with a spy which fails if the decode path matches at all. The
__post_init__ checks the bypass skips are ones decode() has already
established — IPv4's IHL is 4 bits and was rejected below 5, and the
options are sliced to exactly ihl * 4 bytes after the buffer was
confirmed to hold them — noted at each site and in _base.py under
"Decode-path construction". A further test compares decoded instances
against constructed ones, so a field the bypass forgot to set would
surface immediately rather than lurk.
Measured on this machine (200k calls, best of run):
Ethernet.decode 2330 -> 830 ns (2.8x)
IPv4.decode 4936 -> 2596 ns (1.9x)
corpus walk 76,500 -> 113,200 frames/sec (1.48x)
That is ~3.8 us saved per Ethernet+IPv4 frame, against the ~2.3 us the
issue attributes to the regexes alone: skipping the whole constructor
path, rather than only the validators, accounts for the difference.
Closes#84.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QJnVMNGwTRDktC4rkABtgt
@EONRaider
EONRaider merged commit b2c2484 into masterSep 1, 2026
6 checks passed
Sign up for freeto 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.

Stop re-validating addresses the decoder just generated

2 participants

@EONRaider@claude