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 ).
5 use proc_macro2
::TokenStream
as TokenStream2
;
8 use crate::utils
::{self, FieldInfo}
;
9 use syn
::spanned
::Spanned
;
10 use syn
::{Data, DeriveInput, Error}
;
12 pub fn derive_impl(input
: &DeriveInput
) -> TokenStream2
{
13 if !utils
::has_valid_repr(&input
.attrs
, |r
| r
== "packed" || r
== "transparent") {
16 "derive(ULE) must be applied to a #[repr(packed)] or #[repr(transparent)] type",
20 if input
.generics
.type_params().next().is_some()
21 || input
.generics
.lifetimes().next().is_some()
22 || input
.generics
.const_params().next().is_some()
25 input
.generics
.span(),
26 "derive(ULE) must be applied to a struct without any generics",
30 let struc
= if let Data
::Struct(ref s
) = input
.data
{
31 if s
.fields
.iter().next().is_none() {
34 "derive(ULE) must be applied to a non-empty struct",
40 return Error
::new(input
.span(), "derive(ULE) must be applied to a struct")
44 let fields
= FieldInfo
::make_list(struc
.fields
.iter());
45 let (validators
, remaining_offset
) = generate_ule_validators(&fields
);
47 let name
= &input
.ident
;
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]
59 unsafe impl zerovec
::ule
::ULE
for #name {
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()));
68 #[allow(clippy::indexing_slicing)] // We're slicing a chunk of known size
69 for chunk
in bytes
.chunks_exact(SIZE
) {
71 debug_assert_eq
!(#remaining_offset, SIZE);
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.
82 pub(crate) fn generate_ule_validators(
84 // (validators, remaining_offset)
85 ) -> (TokenStream2
, syn
::Ident
) {
86 utils
::generate_per_field_offsets(fields
, false, |field
, prev_offset_ident
, size_ident
| {
87 let ty
= &field
.field
.ty
;
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])?;
95 /// Make corresponding ULE fields for each field
96 pub(crate) fn make_ule_fields(fields
: &[FieldInfo
]) -> Vec
<TokenStream2
> {
100 let ty
= &f
.field
.ty
;
101 let ty
= quote
!(<#ty as zerovec::ule::AsULE>::ULE);
102 let setter
= f
.setter();
103 let vis
= &f
.field
.vis
;
104 quote
!(#vis #setter #ty)