summaryrefslogtreecommitdiffstats
path: root/vendor/zerovec-derive/src/ule.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/zerovec-derive/src/ule.rs')
-rw-r--r--vendor/zerovec-derive/src/ule.rs107
1 files changed, 107 insertions, 0 deletions
diff --git a/vendor/zerovec-derive/src/ule.rs b/vendor/zerovec-derive/src/ule.rs
new file mode 100644
index 000000000..6a03c008f
--- /dev/null
+++ b/vendor/zerovec-derive/src/ule.rs
@@ -0,0 +1,107 @@
+// This file is part of ICU4X. For terms of use, please see the file
+// called LICENSE at the top level of the ICU4X source tree
+// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
+
+use proc_macro2::TokenStream as TokenStream2;
+use quote::quote;
+
+use crate::utils::{self, FieldInfo};
+use syn::spanned::Spanned;
+use syn::{Data, DeriveInput, Error};
+
+pub fn derive_impl(input: &DeriveInput) -> TokenStream2 {
+ if !utils::has_valid_repr(&input.attrs, |r| r == "packed" || r == "transparent") {
+ return Error::new(
+ input.span(),
+ "derive(ULE) must be applied to a #[repr(packed)] or #[repr(transparent)] type",
+ )
+ .to_compile_error();
+ }
+ if input.generics.type_params().next().is_some()
+ || input.generics.lifetimes().next().is_some()
+ || input.generics.const_params().next().is_some()
+ {
+ return Error::new(
+ input.generics.span(),
+ "derive(ULE) must be applied to a struct without any generics",
+ )
+ .to_compile_error();
+ }
+ let struc = if let Data::Struct(ref s) = input.data {
+ if s.fields.iter().next().is_none() {
+ return Error::new(
+ input.span(),
+ "derive(ULE) must be applied to a non-empty struct",
+ )
+ .to_compile_error();
+ }
+ s
+ } else {
+ return Error::new(input.span(), "derive(ULE) must be applied to a struct")
+ .to_compile_error();
+ };
+
+ let fields = FieldInfo::make_list(struc.fields.iter());
+ let (validators, remaining_offset) = generate_ule_validators(&fields);
+
+ let name = &input.ident;
+
+ // Safety (based on the safety checklist on the ULE trait):
+ // 1. #name does not include any uninitialized or padding bytes.
+ // (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
+ // 2. #name is aligned to 1 byte.
+ // (achieved by enforcing #[repr(transparent)] or #[repr(packed)] on a struct of only ULE types)
+ // 3. The impl of validate_byte_slice() returns an error if any byte is not valid.
+ // 4. The impl of validate_byte_slice() returns an error if there are extra bytes.
+ // 5. The other ULE methods use the default impl.
+ // 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]
+ quote! {
+ unsafe impl zerovec::ule::ULE for #name {
+ #[inline]
+ fn validate_byte_slice(bytes: &[u8]) -> Result<(), zerovec::ZeroVecError> {
+ const SIZE: usize = ::core::mem::size_of::<#name>();
+ #[allow(clippy::modulo_one)]
+ if bytes.len() % SIZE != 0 {
+ return Err(zerovec::ZeroVecError::length::<Self>(bytes.len()));
+ }
+ // Validate the bytes
+ #[allow(clippy::indexing_slicing)] // We're slicing a chunk of known size
+ for chunk in bytes.chunks_exact(SIZE) {
+ #validators
+ debug_assert_eq!(#remaining_offset, SIZE);
+ }
+ Ok(())
+ }
+ }
+ }
+}
+
+/// Given an slice over ULE struct fields, returns code validating that a slice variable `bytes` contains valid instances of those ULE types
+/// in order, plus the byte offset of any remaining unvalidated bytes. ULE types should not have any remaining bytes, but VarULE types will since
+/// the last field is the unsized one.
+pub(crate) fn generate_ule_validators(
+ fields: &[FieldInfo],
+ // (validators, remaining_offset)
+) -> (TokenStream2, syn::Ident) {
+ utils::generate_per_field_offsets(fields, false, |field, prev_offset_ident, size_ident| {
+ let ty = &field.field.ty;
+ quote! {
+ #[allow(clippy::indexing_slicing)] // generate_per_field_offsets produces valid indices
+ <#ty as zerovec::ule::ULE>::validate_byte_slice(&bytes[#prev_offset_ident .. #prev_offset_ident + #size_ident])?;
+ }
+ })
+}
+
+/// Make corresponding ULE fields for each field
+pub(crate) fn make_ule_fields(fields: &[FieldInfo]) -> Vec<TokenStream2> {
+ fields
+ .iter()
+ .map(|f| {
+ let ty = &f.field.ty;
+ let ty = quote!(<#ty as zerovec::ule::AsULE>::ULE);
+ let setter = f.setter();
+ let vis = &f.field.vis;
+ quote!(#vis #setter #ty)
+ })
+ .collect::<Vec<_>>()
+}