• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
5 use crate::utils::{self, FieldInfo};
6 use proc_macro2::Span;
7 use proc_macro2::TokenStream as TokenStream2;
8 use quote::quote;
9 use syn::spanned::Spanned;
10 use syn::{Data, DeriveInput, Error, Ident};
11 
12 /// Implementation for derive(VarULE). `custom_varule_validator` validates the last field bytes `last_field_bytes`
13 /// if specified, if not, the VarULE implementation will be used.
derive_impl( input: &DeriveInput, custom_varule_validator: Option<TokenStream2>, ) -> TokenStream214 pub fn derive_impl(
15     input: &DeriveInput,
16     custom_varule_validator: Option<TokenStream2>,
17 ) -> TokenStream2 {
18     if !utils::ReprInfo::compute(&input.attrs).cpacked_or_transparent() {
19         return Error::new(
20             input.span(),
21             "derive(VarULE) must be applied to a #[repr(C, packed)] or #[repr(transparent)] type",
22         )
23         .to_compile_error();
24     }
25     if input.generics.type_params().next().is_some()
26         || input.generics.lifetimes().next().is_some()
27         || input.generics.const_params().next().is_some()
28     {
29         return Error::new(
30             input.generics.span(),
31             "derive(VarULE) must be applied to a struct without any generics",
32         )
33         .to_compile_error();
34     }
35     let struc = if let Data::Struct(ref s) = input.data {
36         if s.fields.iter().next().is_none() {
37             return Error::new(
38                 input.span(),
39                 "derive(VarULE) must be applied to a non-empty struct",
40             )
41             .to_compile_error();
42         }
43         s
44     } else {
45         return Error::new(input.span(), "derive(VarULE) must be applied to a struct")
46             .to_compile_error();
47     };
48 
49     let n_fields = struc.fields.len();
50 
51     let ule_fields = FieldInfo::make_list(struc.fields.iter().take(n_fields - 1));
52 
53     let sizes = ule_fields.iter().map(|f| {
54         let ty = &f.field.ty;
55         quote!(::core::mem::size_of::<#ty>())
56     });
57     let (validators, remaining_offset) = if n_fields > 1 {
58         // generate ULE validators
59         crate::ule::generate_ule_validators(&ule_fields)
60     } else {
61         // no ULE subfields
62         (
63             quote!(
64                 const ZERO: usize = 0;
65             ),
66             Ident::new("ZERO", Span::call_site()),
67         )
68     };
69 
70     let unsized_field = &struc
71         .fields
72         .iter()
73         .next_back()
74         .expect("Already verified that struct is not empty")
75         .ty;
76 
77     let name = &input.ident;
78     let ule_size = Ident::new(
79         &format!("__IMPL_VarULE_FOR_{name}_ULE_SIZE"),
80         Span::call_site(),
81     );
82 
83     let last_field_validator = if let Some(custom_varule_validator) = custom_varule_validator {
84         custom_varule_validator
85     } else {
86         quote!(<#unsized_field as zerovec::ule::VarULE>::validate_bytes(last_field_bytes)?;)
87     };
88 
89     // Safety (based on the safety checklist on the ULE trait):
90     //  1. #name does not include any uninitialized or padding bytes
91     //     (achieved by enforcing #[repr(transparent)] or #[repr(C, packed)] on a struct of only ULE types)
92     //  2. #name is aligned to 1 byte.
93     //     (achieved by enforcing #[repr(transparent)] or #[repr(C, packed)] on a struct of only ULE types)
94     //  3. The impl of `validate_bytes()` returns an error if any byte is not valid.
95     //  4. The impl of `validate_bytes()` returns an error if the slice cannot be used in its entirety
96     //  5. The impl of `from_bytes_unchecked()` returns a reference to the same data.
97     //  6. The other VarULE methods use the default impl
98     //  7. [This impl does not enforce the non-safety equality constraint, it is up to the user to do so, ideally via a custom derive]
99     quote! {
100         // The size of the ULE section of this type
101         const #ule_size: usize = 0 #(+ #sizes)*;
102         unsafe impl zerovec::ule::VarULE for #name {
103             #[inline]
104             fn validate_bytes(bytes: &[u8]) -> Result<(), zerovec::ule::UleError> {
105                 debug_assert_eq!(#remaining_offset, #ule_size);
106 
107                 let Some(last_field_bytes) = bytes.get(#remaining_offset..) else {
108                     return Err(zerovec::ule::UleError::parse::<Self>());
109                 };
110                 #validators
111                 #last_field_validator
112                 Ok(())
113             }
114             #[inline]
115             unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
116                 // just the unsized part
117                 // Safety: The invariants of this function allow us to assume bytes is valid, and
118                 // having at least #ule_size bytes is a validity constraint for the ULE type.
119                 let unsized_bytes = bytes.get_unchecked(#ule_size..);
120                 let unsized_ref = <#unsized_field as zerovec::ule::VarULE>::from_bytes_unchecked(unsized_bytes);
121                 // We should use the pointer metadata APIs here when they are stable: https://github.com/rust-lang/rust/issues/81513
122                 // For now we rely on all DST metadata being a usize to extract it via a fake slice pointer
123                 let (_ptr, metadata): (usize, usize) = ::core::mem::transmute(unsized_ref);
124                 let entire_struct_as_slice: *const [u8] = ::core::slice::from_raw_parts(bytes.as_ptr(), metadata);
125                 &*(entire_struct_as_slice as *const Self)
126             }
127         }
128     }
129 }
130