google / google/zerocopy

Expose layout `fn`s in `KnownLayout`

Open
#2,541 2 comments 0 reactions 0 assignees View on GitHub
customer-request
Dominant language
Rust
Stars
2.6k
Forks
179
Avg merge
1d 19h
Merged PRs (30d)
29

Description

Until solutions like #2490 are present for DST construction (and even after), it'd be extremely convenient to get the correct `Layout` for a given DST with specific tail slice length, considering all of the challenges present in doing that calculation correctly.

So, I propose these operations be exposed on `KnownLayout` for users:

- Get the `Layout` for a given `usize` trailing slice len (`where PointerMetadata=usize`).
- Extract the pointer metadata.
- Construct a raw pointer with the given pointer metadata.

Consider this utility type

```rust
#[derive(Debug, Eq, IntoBytes, Immutable, PartialEq)]
#[repr(C)]
pub struct TpmRequestMessage {
/// The request header
header: TpmRequestHeader,

/// Contains fields specific to the type of request being sent
pub request: T,
}
pub trait TpmvRequest: IntoBytes + Immutable + Unaligned {
const TAG: U16;
const COMMAND_CODE: U16;
const ORDINAL: U32;
type TpmvResponse: FromBytes + ?Sized + Unaligned;
}
```

How does one create a constructor for `Box>` given a `&T` or a length of trailing slice in `T`? It requires manual calculation and being sure about the properties of the `TpmvRequest` (here bounded by `Unaligned`). If I have just a `&T` there's some math, but it still requires `transmute`ing and assuming layout of raw pointers (`SAFETY` docs excluded):

```rust

impl + 'static + ?Sized>
TpmRequestMessage
{
pub fn new_box(request: &T) -> Box {
let (layout, offset) =
Layout::new::().extend(Layout::for_value(request)).unwrap();
let p = unsafe { std::alloc::alloc(layout) };
if p.is_null() {
std::alloc::handle_alloc_error(layout)
}
// There is no less disgusting way to access this `usize`:
// Rust can't know that `T` has the correct metadata for an `as` cast.
let request_raw: *const [u8] = transmute(request);
let trailing_len = request_raw.len();

unsafe {
*p.cast() = TpmRequestHeader::for_request::();
// The `'static` bound limits bad cases like copying `Option<&T>` which can be
// IntoBytes and TryFromBytes, but `try_from_bytes` would reject converting Some.
// It might be better to check if you can `TryFromBytes` `request.as_bytes()`?
p.offset(offset).copy_from_nonoverlapping(
std::ptr::from_ref(request).cast(),
std::mem::size_of_val(request),
);
}

let out_slice = std::ptr::slice_from_raw_parts_mut(p, trailing_len);
unsafe {
let out_ptr: *mut Self = transmute(out_slice);
Box::from_raw(out_ptr)
}
}
}
```

But if I just want to ~construct a zeroed `Box>` I'm very out of luck on stable~ (use `FromZeroes::new_box_zeroed_with_elems`) - I need to essentially reimplement the DST calculation done by `KnownLayout`. This results in the temptation to ignore `#[doc(hidden)]`.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.