zerovec_derive/
ule.rs

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::ReprInfo::compute(&input.attrs).cpacked_or_transparent() {
14        return Error::new(
15            input.span(),
16            "derive(ULE) must be applied to a #[repr(C, 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(C, packed)] on a struct of only ULE types)
52    //  2. #name is aligned to 1 byte.
53    //     (achieved by enforcing #[repr(transparent)] or #[repr(C, packed)] on a struct of only ULE types)
54    //  3. The impl of validate_bytes() returns an error if any byte is not valid.
55    //  4. The impl of validate_bytes() 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    {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "unsafe");
    ::quote::__private::push_ident(&mut _s, "impl");
    ::quote::__private::push_ident(&mut _s, "zerovec");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ule");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ULE");
    ::quote::__private::push_ident(&mut _s, "for");
    ::quote::ToTokens::to_tokens(&name, &mut _s);
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_pound(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Bracket,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "inline");
                    _s
                });
            ::quote::__private::push_ident(&mut _s, "fn");
            ::quote::__private::push_ident(&mut _s, "validate_bytes");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "bytes");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_and(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "u8");
                            _s
                        });
                    _s
                });
            ::quote::__private::push_rarrow(&mut _s);
            ::quote::__private::push_ident(&mut _s, "Result");
            ::quote::__private::push_lt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                ::quote::__private::TokenStream::new());
            ::quote::__private::push_comma(&mut _s);
            ::quote::__private::push_ident(&mut _s, "zerovec");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ule");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "UleError");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Brace,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "const");
                    ::quote::__private::push_ident(&mut _s, "SIZE");
                    ::quote::__private::push_colon(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "usize");
                    ::quote::__private::push_eq(&mut _s);
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "core");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "mem");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "size_of");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::ToTokens::to_tokens(&name, &mut _s);
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_semi(&mut _s);
                    ::quote::__private::push_pound(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "allow");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "clippy");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "modulo_one");
                                    _s
                                });
                            _s
                        });
                    ::quote::__private::push_ident(&mut _s, "if");
                    ::quote::__private::push_ident(&mut _s, "bytes");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "len");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    ::quote::__private::push_rem(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "SIZE");
                    ::quote::__private::push_ne(&mut _s);
                    ::quote::__private::parse(&mut _s, "0");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "return");
                            ::quote::__private::push_ident(&mut _s, "Err");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "zerovec");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "ule");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "UleError");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "length");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_lt(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "Self");
                                    ::quote::__private::push_gt(&mut _s);
                                    ::quote::__private::push_group(&mut _s,
                                        ::quote::__private::Delimiter::Parenthesis,
                                        {
                                            let mut _s = ::quote::__private::TokenStream::new();
                                            ::quote::__private::push_ident(&mut _s, "bytes");
                                            ::quote::__private::push_dot(&mut _s);
                                            ::quote::__private::push_ident(&mut _s, "len");
                                            ::quote::__private::push_group(&mut _s,
                                                ::quote::__private::Delimiter::Parenthesis,
                                                ::quote::__private::TokenStream::new());
                                            _s
                                        });
                                    _s
                                });
                            ::quote::__private::push_semi(&mut _s);
                            _s
                        });
                    ::quote::__private::push_pound(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Bracket,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "allow");
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::__private::push_ident(&mut _s, "clippy");
                                    ::quote::__private::push_colon2(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "indexing_slicing");
                                    _s
                                });
                            _s
                        });
                    ::quote::__private::push_ident(&mut _s, "for");
                    ::quote::__private::push_ident(&mut _s, "chunk");
                    ::quote::__private::push_ident(&mut _s, "in");
                    ::quote::__private::push_ident(&mut _s, "bytes");
                    ::quote::__private::push_dot(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "chunks_exact");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_ident(&mut _s, "SIZE");
                            _s
                        });
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Brace,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::ToTokens::to_tokens(&validators, &mut _s);
                            ::quote::__private::push_ident(&mut _s, "debug_assert_eq");
                            ::quote::__private::push_bang(&mut _s);
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                {
                                    let mut _s = ::quote::__private::TokenStream::new();
                                    ::quote::ToTokens::to_tokens(&remaining_offset, &mut _s);
                                    ::quote::__private::push_comma(&mut _s);
                                    ::quote::__private::push_ident(&mut _s, "SIZE");
                                    _s
                                });
                            ::quote::__private::push_semi(&mut _s);
                            _s
                        });
                    ::quote::__private::push_ident(&mut _s, "Ok");
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        {
                            let mut _s = ::quote::__private::TokenStream::new();
                            ::quote::__private::push_group(&mut _s,
                                ::quote::__private::Delimiter::Parenthesis,
                                ::quote::__private::TokenStream::new());
                            _s
                        });
                    _s
                });
            _s
        });
    _s
}quote! {
59        unsafe impl zerovec::ule::ULE for #name {
60            #[inline]
61            fn validate_bytes(bytes: &[u8]) -> Result<(), zerovec::ule::UleError> {
62                const SIZE: usize = ::core::mem::size_of::<#name>();
63                #[allow(clippy::modulo_one)]
64                if bytes.len() % SIZE != 0 {
65                    return Err(zerovec::ule::UleError::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, false, |field, prev_offset_ident, size_ident| {
87        let ty = &field.field.ty;
88        {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_ident(&mut _s, "if");
    ::quote::__private::push_ident(&mut _s, "let");
    ::quote::__private::push_ident(&mut _s, "Some");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "bytes");
            _s
        });
    ::quote::__private::push_eq(&mut _s);
    ::quote::__private::push_ident(&mut _s, "bytes");
    ::quote::__private::push_dot(&mut _s);
    ::quote::__private::push_ident(&mut _s, "get");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Parenthesis,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::ToTokens::to_tokens(&prev_offset_ident, &mut _s);
            ::quote::__private::push_dot2(&mut _s);
            ::quote::ToTokens::to_tokens(&prev_offset_ident, &mut _s);
            ::quote::__private::push_add(&mut _s);
            ::quote::ToTokens::to_tokens(&size_ident, &mut _s);
            _s
        });
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_lt(&mut _s);
            ::quote::ToTokens::to_tokens(&ty, &mut _s);
            ::quote::__private::push_ident(&mut _s, "as");
            ::quote::__private::push_ident(&mut _s, "zerovec");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ule");
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "ULE");
            ::quote::__private::push_gt(&mut _s);
            ::quote::__private::push_colon2(&mut _s);
            ::quote::__private::push_ident(&mut _s, "validate_bytes");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "bytes");
                    _s
                });
            ::quote::__private::push_question(&mut _s);
            ::quote::__private::push_semi(&mut _s);
            _s
        });
    ::quote::__private::push_ident(&mut _s, "else");
    ::quote::__private::push_group(&mut _s,
        ::quote::__private::Delimiter::Brace,
        {
            let mut _s = ::quote::__private::TokenStream::new();
            ::quote::__private::push_ident(&mut _s, "return");
            ::quote::__private::push_ident(&mut _s, "Err");
            ::quote::__private::push_group(&mut _s,
                ::quote::__private::Delimiter::Parenthesis,
                {
                    let mut _s = ::quote::__private::TokenStream::new();
                    ::quote::__private::push_ident(&mut _s, "zerovec");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "ule");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "UleError");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "parse");
                    ::quote::__private::push_colon2(&mut _s);
                    ::quote::__private::push_lt(&mut _s);
                    ::quote::__private::push_ident(&mut _s, "Self");
                    ::quote::__private::push_gt(&mut _s);
                    ::quote::__private::push_group(&mut _s,
                        ::quote::__private::Delimiter::Parenthesis,
                        ::quote::__private::TokenStream::new());
                    _s
                });
            ::quote::__private::push_semi(&mut _s);
            _s
        });
    _s
}quote! {
89            if let Some(bytes) = bytes.get(#prev_offset_ident .. #prev_offset_ident + #size_ident) {
90                <#ty as zerovec::ule::ULE>::validate_bytes(bytes)?;
91            } else {
92                return Err(zerovec::ule::UleError::parse::<Self>());
93            }
94        }
95    })
96}
97
98/// Make corresponding ULE fields for each field
99pub(crate) fn make_ule_fields(fields: &[FieldInfo]) -> Vec<TokenStream2> {
100    fields
101        .iter()
102        .map(|f| {
103            let ty = &f.field.ty;
104            let ty = {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::__private::push_lt(&mut _s);
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    ::quote::__private::push_ident(&mut _s, "as");
    ::quote::__private::push_ident(&mut _s, "zerovec");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ule");
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "AsULE");
    ::quote::__private::push_gt(&mut _s);
    ::quote::__private::push_colon2(&mut _s);
    ::quote::__private::push_ident(&mut _s, "ULE");
    _s
}quote!(<#ty as zerovec::ule::AsULE>::ULE);
105            let setter = f.setter();
106            let vis = &f.field.vis;
107            {
    let mut _s = ::quote::__private::TokenStream::new();
    ::quote::ToTokens::to_tokens(&vis, &mut _s);
    ::quote::ToTokens::to_tokens(&setter, &mut _s);
    ::quote::ToTokens::to_tokens(&ty, &mut _s);
    _s
}quote!(#vis #setter #ty)
108        })
109        .collect::<Vec<_>>()
110}