Support eliding alignment check when reading from aligned buffer
Opened this issue · 0 comments
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.