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) -> Result<TokenStream, Error> { 19 Ok(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 ctx 101 .error_or_skip(Error::new(Span::call_site(), "can only be applied to structs")); 102 } 103 }; 104 105 if repr.get_packed().is_some() { 106 return ctx.error_or_skip(Error::new( 107 Span::call_site(), 108 "must not have #[repr(packed)] attribute", 109 )); 110 } 111 112 if !(repr.is_c() || repr.is_transparent()) { 113 return ctx.error_or_skip(Error::new( 114 Span::call_site(), 115 "must have #[repr(C)] or #[repr(transparent)] in order to guarantee this type's layout is splitable", 116 )); 117 } 118 119 let fields = ctx.ast.data.fields(); 120 let trailing_field = if let Some(((_, _, trailing_field), _)) = fields.split_last() { 121 trailing_field 122 } else { 123 return ctx.error_or_skip(Error::new(Span::call_site(), "must at least one field")); 124 }; 125 126 let zerocopy_crate = &ctx.zerocopy_crate; 127 // SAFETY: `#ty`, per the above checks, is `repr(C)` or `repr(transparent)` 128 // and is not packed; its trailing field is guaranteed to be well-aligned 129 // for its type. By invariant on `FieldBounds::TRAILING_SELF`, the trailing 130 // slice of the trailing field is also well-aligned for its type. 131 Ok(ImplBlockBuilder::new(ctx, &ctx.ast.data, Trait::SplitAt, FieldBounds::TRAILING_SELF) 132 .inner_extras(quote! { 133 type Elem = <#trailing_field as #zerocopy_crate::SplitAt>::Elem; 134 }) 135 .build()) 136 } 137