Skip to content

codec: replace codec! with a Codec derive [improved version] - #2

Open
badicsalex wants to merge 11 commits into
mainfrom
SFT-8112-codec-derive-ref
Open

badicsalex wants to merge 11 commits into
mainfrom
SFT-8112-codec-derive-ref

Conversation

@badicsalex

Copy link
Copy Markdown
Contributor

Similar to #1 , but with changes going in a different direction, like Ref<> instead of raw slices in ref structs.

Submitted as a separate PR (even though it builds on #1), because it has less of a diff compared to main.

badicsalex and others added 10 commits October 10, 2026 19:57
A corrupt block count made the receiver reserve 16 bytes per block up
front, which overflows the capacity on 32-bit targets and panics.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Only the last byte can overflow, so it's the only one checked.

Redundant encodings such as [0x80, 0x00] decode fine. Rejecting them is
not really necessary, as nothing else makes encodings unique either:
non-frozen structs skip unknown trailing fields. And the check costs
measurably, 6-19 ns per multi-byte varint on the device.

On the device, this cuts the time to decode small messages into their
borrowed form by a third to a half.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The derive is meant for keyos IPC, so by default it prefixes structs and
enum variants with their length, which lets old and new versions of a
type decode each other. QuantumLink's own wire types use it frozen,
which keeps the codec! layout byte for byte.

Decode no longer abstracts over the byte container. The borrowed form
points into a &[u8] instead, and the frame parser slices stream payloads
out of the record's Bytes itself.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Applied without the alignment commit, so Element and the borrowed byte
vectors and arrays stay.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fsm decodes a record from a BytesMut, decrypts the ciphertext it lends
in place and freezes it into the plaintext, instead of slicing its
buffer by range. Session frames are read from that Bytes, which lends
their stream data directly instead of going through slice_ref.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Lists and arrays of fixed-width integers are padded to a multiple of
their size from the start of the encoding, so a borrowed form can lend
them as a Ref<B, [u32]> from an aligned buffer. That position has to be
known on both sides, so encoding goes through a Writer that counts it
instead of a BufMut, and nested readers keep the outer one's. The zeros
before and after a list always add up to size - 1, which keeps
encoded_len independent of where the list lands.

Bytes are the integers that need no padding, so byte vectors and arrays
go through the same impls, and bytemuck checks the casts instead of
unsafe code.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A BytesMut no longer lends missing arrays from a fresh allocation, which
wasn't guaranteed to be aligned for integers. Like a &mut [u8], it fails
instead. The derive names the borrowed form's input apart from
everything in the item, so a type called B no longer captures it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants