1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5use proc_macro2::TokenStream as TokenStream2;
6use quote::quote;
7
8use crate::utils::{self, FieldInfo};
9use syn::spanned::Spanned;
10use syn::{Data, DeriveInput, Error};
11
12pub fn derive_impl(input: &DeriveInput) -> TokenStream2 {
13 if !utils::has_valid_repr(&input.attrs, |r| r == "packed" || r == "transparent") {
14 return Error::new(
15 input.span(),
16 "derive(ULE) must be applied to a #[repr(packed)] or #[repr(transparent)] type",
17 )
18 .to_compile_error();
19 }
20 if input.generics.type_params().next().is_some()
21 || input.generics.lifetimes().next().is_some()
22 || input.generics.const_params().next().is_some()
23 {
24 return Error::new(
25 input.generics.span(),
26 "derive(ULE) must be applied to a struct without any generics",
27 )
28 .to_compile_error();
29 }
30 let struc = if let Data::Struct(ref s) = input.data {
31 if s.fields.iter().next().is_none() {
32 return Error::new(
33 input.span(),
34 "derive(ULE) must be applied to a non-empty struct",
35 )
36 .to_compile_error();
37 }
38 s
39 } else {
40 return Error::new(input.span(), "derive(ULE) must be applied to a struct")
41 .to_compile_error();
42 };
43
44 let fields = FieldInfo::make_list(struc.fields.iter());
45 let (validators, remaining_offset) = generate_ule_validators(&fields);
46
47 let name = &input.ident;
48
49 // Safety (based on the safety checklist on the ULE trait):
50 // 1. #name does not include any uninitialized or padding bytes.
51 // (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
52 // 2. #name is aligned to 1 byte.
53 // (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
54 // 3. The impl of validate_byte_slice() returns an error if any byte is not valid.
55 // 4. The impl of validate_byte_slice() returns an error if there are extra bytes.
56 // 5. The other ULE methods use the default impl.
57 // 6. [This impl does not enforce the non-safety equality constraint, it is up to the user to do so, ideally via a custom derive]
58 quote! {
59 unsafe impl zerovec::ule::ULE for #name {
60 #[inline]
61 fn validate_byte_slice(bytes: &[u8]) -> Result<(), zerovec::ZeroVecError> {
62 const SIZE: usize = ::core::mem::size_of::<#name>();
63 #[allow(clippy::modulo_one)]
64 if bytes.len() % SIZE != 0 {
65 return Err(zerovec::ZeroVecError::length::<Self>(bytes.len()));
66 }
67 // Validate the bytes
68 #[allow(clippy::indexing_slicing)] // We're slicing a chunk of known size
69 for chunk in bytes.chunks_exact(SIZE) {
70 #validators
71 debug_assert_eq!(#remaining_offset, SIZE);
72 }
73 Ok(())
74 }
75 }
76 }
77}
78
79/// Given an slice over ULE struct fields, returns code validating that a slice variable `bytes` contains valid instances of those ULE types
80/// in order, plus the byte offset of any remaining unvalidated bytes. ULE types should not have any remaining bytes, but VarULE types will since
81/// the last field is the unsized one.
82pub(crate) fn generate_ule_validators(
83 fields: &[FieldInfo],
84 // (validators, remaining_offset)
85) -> (TokenStream2, syn::Ident) {
86 utils::generate_per_field_offsets(fields, fields_are_asule:false, |field: &FieldInfo<'_>, prev_offset_ident: &Ident, size_ident: &Ident| {
87 let ty: &Type = &field.field.ty;
88 quote! {
89 #[allow(clippy::indexing_slicing)] // generate_per_field_offsets produces valid indices
90 <#ty as zerovec::ule::ULE>::validate_byte_slice(&bytes[#prev_offset_ident .. #prev_offset_ident + #size_ident])?;
91 }
92 })
93}
94
95/// Make corresponding ULE fields for each field
96pub(crate) fn make_ule_fields(fields: &[FieldInfo]) -> Vec<TokenStream2> {
97 fieldsimpl Iterator
98 .iter()
99 .map(|f: &FieldInfo<'_>| {
100 let ty: &Type = &f.field.ty;
101 let ty: TokenStream = quote!(<#ty as zerovec::ule::AsULE>::ULE);
102 let setter: TokenStream = f.setter();
103 let vis: &Visibility = &f.field.vis;
104 quote!(#vis #setter #ty)
105 })
106 .collect::<Vec<_>>()
107}
108