Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support eliding alignment check when reading from aligned buffer #280

Open
joshlf opened this issue Aug 23, 2023 · 0 comments
Open

Support eliding alignment check when reading from aligned buffer #280

joshlf opened this issue Aug 23, 2023 · 0 comments

Comments

@joshlf
Copy link
Member

joshlf commented Aug 23, 2023

Any design for this issue will interact with other issues tracked by #885; make sure to read that issue when tackling this one.

Currently, we special-case types with alignment 1 by allowing them to implement the Unaligned trait, and use that trait to elide an alignment check when attempting to read a type from a [u8], which provides no alignment guarantees (e.g., Ref constructors with unaligned in their name).

If we add an Align type, it will be possible to express the type of a [u8] which has an alignment guarantee greater than 1. It would be useful if we could also elide alignment checks when parsing non-Unaligned types from these aligned buffers so long as the buffer's alignment is at least as large as the type's alignment.

This could potentially result in more optimal code for users like packet-formats, which currently have to assume that all loads may be unaligned (e.g. using unaligned types from the byteorder module).

Alignment reasoning

In general, we need the ability to understand that &Src -> &Dst doesn't require an alignment check. This could either be thanks to knowing that align_of::<Src>() >= align_of::<Dst>() (#1316), or by forcing it using an Align type (#249).

Use in buffers

I can imagine two shapes for this API:

Buffer returned by value

For example, Ref and FromBytes methods often consume a &[u8] and return (&T, &[u8]) (or a B: ByteSlice instead of &[u8]). We could imagine instead requiring the input be Align<[u8], {align_of::<T>()}> and returning (&T, Align<[u8], {align_of::<T>()}>)

Buffer alignment unmodified by &mut methods

If we also support a trait or traits in the style of packet::BufferView, we could imagine adding an alignment invariant:

trait Buffer<const ALIGN: usize> {
    // TODO: How to express in the type system that `n % ALIGN == 0`? Or maybe
    // just validate at runtime?
    fn take_bytes_front(&mut self, n: usize) -> Option<&Align<[u8], {ALIGN}>>;

    // Alternatively, using a `ByteArray` API a la #248:
    fn take_bytes_front<T>(&mut self) -> Option<&Align<ByteArray<T>, {ALIGN}>>
    where
        size_of::<ByteArray<T>>() % ALIGN == 0; // TODO: How to express this in the type system?

    // Since `align_of::<T>() <= N`, this can return only `SizeError`.
    fn take_obj_front<T: FromBytes>(&mut self) -> Result<&T, SizeError>
    where
        T: AlignLtEq<{N}>, // Supported by #1316
        size_of::<T>() % ALIGN == 0; // TODO: How to express this in the type system?
    {
        ...
    }
}

take_bytes_front requires the alignment as its precondition, and preserves it as its post-condition.

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

No branches or pull requests

1 participant