mirror of
https://github.com/slint-ui/slint.git
synced 2025-10-01 14:21:16 +00:00
199 lines
5.9 KiB
Rust
199 lines
5.9 KiB
Rust
/*!
|
|
This crate allow to get the offset of a field of a structure in a const or static context.
|
|
|
|
To be used re-exported from the `const_field_offset` crate
|
|
|
|
*/
|
|
extern crate proc_macro;
|
|
|
|
use proc_macro::TokenStream;
|
|
use quote::quote;
|
|
use syn::{parse_macro_input, spanned::Spanned, DeriveInput};
|
|
|
|
/**
|
|
|
|
The macro FieldOffsets adds a `const fn field_offsets()` associated function to the struct, that
|
|
returns an object which has fields with the same name as the fields of the original struct,
|
|
each field is of type `const_field_offset::FieldOffset`
|
|
|
|
```rust
|
|
use const_field_offset::FieldOffsets;
|
|
#[repr(C)]
|
|
#[derive(FieldOffsets)]
|
|
struct Foo {
|
|
field_1 : u8,
|
|
field_2 : u32,
|
|
}
|
|
|
|
const FOO : usize = Foo::field_offsets().field_2.get_byte_offset();
|
|
assert_eq!(FOO, 4);
|
|
|
|
// This would not work on stable rust at the moment (rust 1.43)
|
|
// const FOO : usize = memofsets::offsetof!(Foo, field_2);
|
|
```
|
|
|
|
## limitations
|
|
|
|
Only work with named #[repr(C)] structures.
|
|
|
|
## Attributes
|
|
|
|
In case the `const-field-offset` crate is re-exported, it is possible to
|
|
specify the crate name using the `const_field_offset` attribute.
|
|
|
|
```rust
|
|
// suppose you re-export the const_field_offset create from a different module
|
|
mod xxx { pub use const_field_offset as cfo; }
|
|
#[repr(C)]
|
|
#[derive(xxx::cfo::FieldOffsets)]
|
|
#[const_field_offset(xxx::cfo)]
|
|
struct Foo {
|
|
field_1 : u8,
|
|
field_2 : u32,
|
|
}
|
|
```
|
|
|
|
*/
|
|
#[proc_macro_derive(FieldOffsets, attributes(const_field_offset))]
|
|
pub fn const_field_offset(input: TokenStream) -> TokenStream {
|
|
let input = parse_macro_input!(input as DeriveInput);
|
|
|
|
let mut has_repr_c = false;
|
|
for a in &input.attrs {
|
|
if let Some(i) = a.path.get_ident() {
|
|
if i == "repr" {
|
|
match a.tokens.to_string().as_str() {
|
|
"(C)" => has_repr_c = true,
|
|
"(packed)" => {
|
|
return TokenStream::from(quote!(
|
|
compile_error! {"FieldOffsets does not work on #[repr(packed)]"}
|
|
))
|
|
}
|
|
_ => (),
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if !has_repr_c {
|
|
return TokenStream::from(
|
|
quote! {compile_error!{"FieldOffsets inly work if the structure repr(C)"}},
|
|
);
|
|
}
|
|
|
|
if input.attrs.iter().any(|a| {
|
|
if let Some(i) = a.path.get_ident() {
|
|
i == "repr" && a.tokens.to_string() == "(packed)"
|
|
} else {
|
|
false
|
|
}
|
|
}) {
|
|
return TokenStream::from(quote! {compile_error!{"Does not work if "}});
|
|
}
|
|
|
|
let struct_name = input.ident;
|
|
let field_struct_name = quote::format_ident!("{}FieldsOffsets", struct_name);
|
|
|
|
let (fields, types, vis) = if let syn::Data::Struct(s) = &input.data {
|
|
if let syn::Fields::Named(n) = &s.fields {
|
|
let (f, tv): (Vec<_>, Vec<_>) =
|
|
n.named.iter().map(|f| (&f.ident, (&f.ty, &f.vis))).unzip();
|
|
let (t, v): (Vec<_>, Vec<_>) = tv.into_iter().unzip();
|
|
(f, t, v)
|
|
} else {
|
|
return TokenStream::from(quote! {compile_error!{"Only work for named fields"}});
|
|
}
|
|
} else {
|
|
return TokenStream::from(quote! {compile_error!("Only work for struct")});
|
|
};
|
|
|
|
let crate_ = input
|
|
.attrs
|
|
.iter()
|
|
.find(|a| a.path.get_ident().map_or(false, |i| i == "const_field_offset"))
|
|
.map(|a| {
|
|
a.tokens
|
|
.clone()
|
|
.into_iter()
|
|
.next()
|
|
.and_then(|tt| match tt {
|
|
proc_macro2::TokenTree::Group(g) => {
|
|
if g.delimiter() == proc_macro2::Delimiter::Parenthesis {
|
|
Some(g.stream())
|
|
} else {
|
|
None
|
|
}
|
|
}
|
|
_ => None,
|
|
})
|
|
.ok_or_else(|| {
|
|
syn::Error::new(a.span(), "The argument must be a path to the crate")
|
|
})
|
|
})
|
|
.unwrap_or_else(|| Ok(quote!(const_field_offset)));
|
|
let crate_ = match crate_ {
|
|
Ok(crate_) => crate_,
|
|
Err(e) => return e.to_compile_error().into(),
|
|
};
|
|
|
|
let doc = format!(
|
|
"Helper struct containing the offsets of the fields of the struct `{}`",
|
|
struct_name
|
|
);
|
|
|
|
// Build the output, possibly using quasi-quotation
|
|
let expanded = quote! {
|
|
#[doc = #doc]
|
|
///
|
|
/// Generated from the derive macro `const-field-offset::FieldOffsets`
|
|
#[allow(missing_docs, non_camel_case_types)]
|
|
pub struct #field_struct_name {
|
|
#(#vis #fields : #crate_::FieldOffset<#struct_name, #types>,)*
|
|
}
|
|
|
|
impl #struct_name {
|
|
/// Return a struct containing the offset of for the fields of this struct
|
|
pub const fn field_offsets() -> #field_struct_name {
|
|
let mut len = 0usize;
|
|
#field_struct_name {
|
|
#( #fields : {
|
|
let align = ::core::mem::align_of::<#types>();
|
|
// from Layout::padding_needed_for which is not yet stable
|
|
let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1);
|
|
len = len_rounded_up + ::core::mem::size_of::<#types>();
|
|
/// Safety: According to the rules of repr(C), this is the right offset
|
|
unsafe { #crate_::FieldOffset::<#struct_name, #types>::new_from_offset(len_rounded_up) }
|
|
}, )*
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
// Hand the output tokens back to the compiler
|
|
TokenStream::from(expanded)
|
|
}
|
|
|
|
/**
|
|
```compile_fail
|
|
use const_field_offset::*;
|
|
#[derive(FieldOffsets)]
|
|
struct Foo {
|
|
x: u32,
|
|
}
|
|
```
|
|
*/
|
|
#[cfg(doctest)]
|
|
const _NO_REPR_C: u32 = 0;
|
|
|
|
/**
|
|
```compile_fail
|
|
use const_field_offset::*;
|
|
#[derive(FieldOffsets)]
|
|
#[repr(C)]
|
|
#[repr(packed)]
|
|
struct Foo {
|
|
x: u32,
|
|
}
|
|
```
|
|
*/
|
|
#[cfg(doctest)]
|
|
const _REPR_PACKED: u32 = 0;
|