google/zerocopy

features missing to support iroh

dignifiedquire opened this issue · 8 comments

What is the name of your project?
iroh

Please provide a link to your project (GitHub repository, crates.io page, etc).
https://github.com/n0-computer/iroh

What features would you like from zerocopy?

Deriving/implementing FromBytes and AsBytes for structs with trailing byte slices.

#[derive(Debug, Clone, PartialEq, Eq)] // Can't add FromBytes, AsBytes, FromZeroes here 
#[repr(C)]
pub struct RecordIdentifier<B: ByteSlice> {
    namespace: [u8; 32],
    author: [u8; 32],
    key: B,
}

This is to support reading these identifiers from redb.

Thanks for the request!

We're currently implementing something that should support this in #29. You can try this out in our latest 0.8 alpha release. In particular, add #[derive(KnownLayout)] to the top of your struct, and then APIs with a KnownLayout bound such as FromBytes::ref_from should be available to you.

Please note that this is under active development - we're still adding support for some APIs, and APIs are subject to changes (both changes to type signatures and name changes) before we stabilize and release 0.8. That said, we'd love for you to kick the tires and let us know if it works for you or if there are things we should change/add before we release 0.8!

Thanks a lot, I am very close to having things work with the 0.8 branch, the only thing I am missing is a way to get IntoBytes working. I think in theory I could just use slice::from_raw_parts but obviously I would like to avoid having to add unsafe code to achieve this.

#[derive(Debug, PartialEq, Eq, KnownLayout, FromBytes, NoCell, Unaligned)]
#[repr(C)]
pub struct RecordIdentifier {
    namespace: [u8; 32],
    author: [u8; 32],
    key: [u8],
}

impl AsRef<[u8]> for RecordIdentifier {
    fn as_ref(&self) -> &[u8] {
        // How can I do this? 
        todo!()
        // self.as_bytes()
    }
}

I guess this feature is currently dependent on std::mem::size_of_val_raw :/

I wonder if a nightly flag could be added to zerocopy to allow for doing this when using a nightly compiler, as a stopgap until this gets stabilized.

For reference, here is a complete gist that I am using to test this out: https://gist.github.com/dignifiedquire/ea1219ba8de5c02904b2680844759864

If you use #[repr(C, packed)], then you can derive IntoBytes directly. Here's an example from 0.7 (because that's what's available on the Rust Playground; note that AsBytes is only renamed to IntoBytes in 0.8):

use zerocopy::*;

#[derive(AsBytes)]
#[repr(C, packed)]
pub struct RecordIdentifier {
    namespace: [u8; 32],
    author: [u8; 32],
    key: [u8],
}

impl AsRef<[u8]> for RecordIdentifier {
    fn as_ref(&self) -> &[u8] {
        self.as_bytes()
    }
}

Basically what's happening here is that, without repr(packed), the IntoBytes derive isn't smart enough to know that your type will never have padding (padding in DSTs is weird - the trailing padding is calculated dynamically as a function of the trailing slice length). In this particular case, repr(packed) doesn't actually change anything because none of your field types have alignment greater than 1 anyway, but it's a guarantee that the IntoBytes derive knows how to understand.

Perfect, works as I need it. Thanks a lot for the great support. Feel free to close or keep upon until 0.8 is released.

Of course, np! I'm gonna close since this is already on our roadmap, but feel free to open a new issue if other things come up.