1 // SPDX-License-Identifier: (BSD-2-Clause OR Apache-2.0) OR MIT 2 3 pub mod from_bytes; 4 pub mod into_bytes; 5 pub mod known_layout; 6 pub mod try_from_bytes; 7 pub mod unaligned; 8 9 use proc_macro2::{Span, TokenStream}; 10 use quote::quote; 11 use syn::{Data, Error}; 12 13 use crate::{ 14 repr::StructUnionRepr, 15 util::{Ctx, DataExt, FieldBounds, ImplBlockBuilder, Trait}, 16 }; 17 18 pub(crate) fn derive_immutable(ctx: &Ctx, _top_level: Trait) -> TokenStream { 19 match &ctx.ast.data { 20 Data::Struct(strct) => { 21 ImplBlockBuilder::new(ctx, strct, Trait::Immutable, FieldBounds::ALL_SELF).build() 22 } 23 Data::Enum(enm) => { 24 ImplBlockBuilder::new(ctx, enm, Trait::Immutable, FieldBounds::ALL_SELF).build() 25 } 26 Data::Union(unn) => { 27 ImplBlockBuilder::new(ctx, unn, Trait::Immutable, FieldBounds::ALL_SELF).build() 28 } 29 } 30 } 31 32 pub(crate) fn derive_hash(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> { 33 // This doesn't delegate to `impl_block` because `impl_block` assumes it is 34 // deriving a `zerocopy`-defined trait, and these trait impls share a common 35 // shape that `Hash` does not. In particular, `zerocopy` traits contain a 36 // method that only `zerocopy_derive` macros are supposed to implement, and 37 // `impl_block` generating this trait method is incompatible with `Hash`. 38 let type_ident = &ctx.ast.ident; 39 let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl(); 40 let where_predicates = where_clause.map(|clause| &clause.predicates); 41 let zerocopy_crate = &ctx.zerocopy_crate; 42 let core = ctx.core_path(); 43 Ok(quote! { 44 impl #impl_generics #core::hash::Hash for #type_ident #ty_generics 45 where 46 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable, 47 #where_predicates 48 { 49 fn hash<H: #core::hash::Hasher>(&self, state: &mut H) { 50 #core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(self)) 51 } 52 53 fn hash_slice<H: #core::hash::Hasher>(data: &[Self], state: &mut H) { 54 #core::hash::Hasher::write(state, #zerocopy_crate::IntoBytes::as_bytes(data)) 55 } 56 } 57 }) 58 } 59 60 pub(crate) fn derive_eq(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> { 61 // This doesn't delegate to `impl_block` because `impl_block` assumes it is 62 // deriving a `zerocopy`-defined trait, and these trait impls share a common 63 // shape that `Eq` does not. In particular, `zerocopy` traits contain a 64 // method that only `zerocopy_derive` macros are supposed to implement, and 65 // `impl_block` generating this trait method is incompatible with `Eq`. 66 let type_ident = &ctx.ast.ident; 67 let (impl_generics, ty_generics, where_clause) = ctx.ast.generics.split_for_impl(); 68 let where_predicates = where_clause.map(|clause| &clause.predicates); 69 let zerocopy_crate = &ctx.zerocopy_crate; 70 let core = ctx.core_path(); 71 Ok(quote! { 72 impl #impl_generics #core::cmp::PartialEq for #type_ident #ty_generics 73 where 74 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable, 75 #where_predicates 76 { 77 fn eq(&self, other: &Self) -> bool { 78 #core::cmp::PartialEq::eq( 79 #zerocopy_crate::IntoBytes::as_bytes(self), 80 #zerocopy_crate::IntoBytes::as_bytes(other), 81 ) 82 } 83 } 84 85 impl #impl_generics #core::cmp::Eq for #type_ident #ty_generics 86 where 87 Self: #zerocopy_crate::IntoBytes + #zerocopy_crate::Immutable, 88 #where_predicates 89 { 90 } 91 }) 92 } 93 94 pub(crate) fn derive_split_at(ctx: &Ctx, _top_level: Trait) -> Result<TokenStream, Error> { 95 let repr = StructUnionRepr::from_attrs(&ctx.ast.attrs)?; 96 97 match &ctx.ast.data { 98 Data::Struct(_) => {} 99 Data::Enum(_) | Data::Union(_) => { 100 return Err(Error::new(Span::call_site(), "can only be applied to structs")); 101 } 102 }; 103 104 if repr.get_packed().is_some() { 105 return Err(Error::new(Span::call_site(), "must not have #[repr(packed)] attribute")); 106 } 107 108 if !(repr.is_c() || repr.is_transparent()) { 109 return Err(Error::new( 110 Span::call_site(), 111 "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable", 112 )); 113 } 114 115 let fields = ctx.ast.data.fields(); 116 let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() { 117 trailing_field 118 } else { 119 return Err(Error::new(Span::call_site(), "must at least one field")); 120 }; 121 122 let zerocopy_crate = &ctx.zerocopy_crate; 123 // SAFETY: `#ty`, per the above checks, is `repr(C)` or `repr(transparent)` 124 // and is not packed; its trailing field is guaranteed to be well-aligned 125 // for its type. By invariant on `FieldBounds::TRAILING_SELF`, the trailing 126 // slice of the trailing field is also well-aligned for its type. 127 Ok(ImplBlockBuilder::new(ctx, &ctx.ast.data, Trait::SplitAt, FieldBounds::TRAILING_SELF) 128 .inner_extras(quote! { 129 type Elem = <#trailing_field as #zerocopy_crate::SplitAt>::Elem; 130 }) 131 .build()) 132 } 133