Expand description

Byte slices with statically guaranteed alignment

The gvariant crates operates by reinterpreting byte buffers as a Rust type. For these casts to be valid the alignment of the underlying data must be sufficient for the target type. We don’t perform any of our own allocations, relying on data passed from the user, as a result proper alignment of byte buffers is the responsibility of the user.

This library defines a type AlignedSlice<A> which represents an aligned byte buffer aligned to the alignment given by A. A may be:

  • A1 - 1B aligned aka unaligned
  • A2 - 2B aligned
  • A4 - 4B aligned
  • A8 - 8B aligned

As a type parameter the alignment is known statically at compile time. This allows eliding runtime checks.

You can convert AlignedSlices to lower alignments infallibly and for free (using AsAligned::as_aligned and AsAlignedMut::as_aligned_mut). Going in the opposite direction and coming from a plain &[u8] slice requires runtime checks (TryAsAligned::try_as_aligned and TryAsAlignedMut::try_as_aligned_mut) and may require copying the data (copy_to_align).

The AsAligned trait is provided to make accepting any data of the appropriate alignment convenient. For example: use a &impl AsAligned<A2> parameter to accept any data with 2B or greater alignment.

AlignedBuf is provided to make it easy and safe to create aligned buffers. Example reading data from file into aligned buffer:

let mut buf = vec![];
let len = file.read(buf.as_mut())?;
let mut buf : AlignedBuf = buf.into();
Efficiency of statically known alignment

Every GVariant container type has alignment >= any of its children. This means that if a byte slice is aligned for the parent it will still be aligned when we access the children, so we only need to check the alignment once when constructing the buffer rather than on every access.

For example: The structure (nu) (or (i16, u32) in rust terms) has alignment 4: So we create an AlignedSlice<A4> containing the complete 8B structure. The alignment is checked at run-time when we create the slice. The i16 is extracted with data[..2], which still has type AlignedSlice<A4>. The conversion to AlignedSlice<A2> as required by the i16’s doesn’t require any runtime checks.

Serialisation alignment vs. platform alignment requirements

Note: there are two related, but subtly different concepts of alignment at use in this library:

  1. The GVariant serialisation format has a concept of alignment of data types which informs where padding should be placed and affects the size of fixed size structures.
  2. There are the alignment requirements of the types we’re casting to that Rust and the underlying platform requires of us when we cast. These are the values that std::mem::align_of<>() returns.

These alignments are usually the same, but in some circumstances can differ. For example: I believe that on 32-bit x86 align_of<u64>() == 4 - while of course the serialisation format doesn’t depend on the platform in use. This library assumes (and asserts in code) that the former alignment is always >= the latter.

Structs

1-byte alignment e.g. no alignment

2-byte alignment

4-byte alignment

8-byte alignment

A buffer where the pointed to data is guaranteed to have 8B alignment. The length of the data needn’t be a multiple of 8.

Represents a usize that is some multiple of Alignment::ALIGNMENT.

A byte array, but tagged with GVariant alignment

Error returned by TryAsAligned::try_as_aligned and TryAsAlignedMut::try_as_aligned_mut when the passed in slice isn’t appropriately aligned.

Traits

This is a promise that the type is aligned as described by A.

A trait for our alignment structs A1, A2, A4 and A8.

Allows narrowing the alignment of a &AlignedSlice

Allows narrowing the alignment of a &mut AlignedSlice

Allows widening the alignment by performing fallible runtime checks.

Allows widening the alignment by performing fallible runtime checks.

Functions

Construct an AlignedOffset by rounding-up idx until it’s a multiple of A::ALIGNMENT.

Aligns the given data to the alignment as given by the type parameter A.

Get a static reference to an empty AlignedSlice with the given alignment.