Support eliding alignment check when reading from aligned buffer
- Dominant language
- Rust
- Stars
- 2.6k
- Forks
- 179
- Avg merge
- 1d 19h
- Merged PRs (30d)
- 29
Description
*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](https://github.com/google/zerocopy/issues/249), 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](https://fuchsia-docs.firebaseapp.com/rust/packet_formats/index.html), 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::() >= align_of::()` (#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::()}>` and returning `(&T, Align<[u8], {align_of::()}>)`
### 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:
```rust
trait Buffer {
// 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(&mut self) -> Option<&Align, {ALIGN}>>
where
size_of::>() % ALIGN == 0; // TODO: How to express this in the type system?
// Since `align_of::() <= N`, this can return only `SizeError`.
fn take_obj_front(&mut self) -> Result<&T, SizeError>
where
T: AlignLtEq<{N}>, // Supported by #1316
size_of::() % 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.
Contributor guide
Assessment
This issue has not been assessed yet.