2024-07-31 12:20:26 -05:00
|
|
|
// Copyright © 2023 Collabora, Ltd.
|
|
|
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
|
|
|
|
|
|
use proc_macro::TokenStream;
|
|
|
|
|
use proc_macro2::{Span, TokenStream as TokenStream2};
|
|
|
|
|
use syn::*;
|
|
|
|
|
|
|
|
|
|
fn expr_as_usize(expr: &syn::Expr) -> usize {
|
|
|
|
|
let lit = match expr {
|
|
|
|
|
syn::Expr::Lit(lit) => lit,
|
|
|
|
|
_ => panic!("Expected a literal, found an expression"),
|
|
|
|
|
};
|
|
|
|
|
let lit_int = match &lit.lit {
|
|
|
|
|
syn::Lit::Int(i) => i,
|
|
|
|
|
_ => panic!("Expected a literal integer"),
|
|
|
|
|
};
|
|
|
|
|
assert!(lit.attrs.is_empty());
|
|
|
|
|
lit_int
|
|
|
|
|
.base10_parse()
|
|
|
|
|
.expect("Failed to parse integer literal")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn count_type(ty: &Type, slice_type: &str) -> usize {
|
|
|
|
|
match ty {
|
|
|
|
|
syn::Type::Array(a) => {
|
|
|
|
|
let elems = count_type(a.elem.as_ref(), slice_type);
|
|
|
|
|
if elems > 0 {
|
|
|
|
|
elems * expr_as_usize(&a.len)
|
|
|
|
|
} else {
|
|
|
|
|
0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
syn::Type::Path(p) => {
|
|
|
|
|
if p.qself.is_none() && p.path.is_ident(slice_type) {
|
|
|
|
|
1
|
|
|
|
|
} else {
|
|
|
|
|
0
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
_ => 0,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get_attr(field: &Field, attr_name: &str) -> Option<String> {
|
|
|
|
|
for attr in &field.attrs {
|
|
|
|
|
if let Meta::List(ml) = &attr.meta {
|
|
|
|
|
if ml.path.is_ident(attr_name) {
|
|
|
|
|
return Some(format!("{}", ml.tokens));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
None
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn derive_as_slice(
|
|
|
|
|
input: TokenStream,
|
|
|
|
|
slice_type: &str,
|
|
|
|
|
attr_name: &str,
|
|
|
|
|
attr_type: &str,
|
|
|
|
|
) -> TokenStream {
|
|
|
|
|
let DeriveInput {
|
|
|
|
|
attrs, ident, data, ..
|
|
|
|
|
} = parse_macro_input!(input);
|
|
|
|
|
|
|
|
|
|
match data {
|
|
|
|
|
Data::Struct(s) => {
|
|
|
|
|
let mut has_repr_c = false;
|
|
|
|
|
for attr in attrs {
|
2025-10-04 15:35:42 +02:00
|
|
|
if let Meta::List(ml) = attr.meta {
|
|
|
|
|
if ml.path.is_ident("repr")
|
|
|
|
|
&& format!("{}", ml.tokens) == "C"
|
|
|
|
|
{
|
|
|
|
|
has_repr_c = true;
|
2024-07-31 12:20:26 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
assert!(has_repr_c, "Struct must be declared #[repr(C)]");
|
|
|
|
|
|
|
|
|
|
let mut first = None;
|
|
|
|
|
let mut count = 0_usize;
|
|
|
|
|
let mut found_last = false;
|
|
|
|
|
let mut attrs = TokenStream2::new();
|
|
|
|
|
|
|
|
|
|
if let Fields::Named(named) = s.fields {
|
|
|
|
|
for f in named.named {
|
|
|
|
|
let f_count = count_type(&f.ty, slice_type);
|
2025-10-04 16:16:59 +02:00
|
|
|
let f_attr = get_attr(&f, attr_name);
|
2024-07-31 12:20:26 -05:00
|
|
|
|
|
|
|
|
if f_count > 0 {
|
|
|
|
|
assert!(
|
|
|
|
|
!found_last,
|
|
|
|
|
"All fields of type {slice_type} must be consecutive",
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let attr_type =
|
|
|
|
|
Ident::new(attr_type, Span::call_site());
|
|
|
|
|
let f_attr = if let Some(s) = f_attr {
|
|
|
|
|
let s = syn::parse_str::<Ident>(&s).unwrap();
|
|
|
|
|
quote! { #attr_type::#s, }
|
|
|
|
|
} else {
|
|
|
|
|
quote! { #attr_type::DEFAULT, }
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
first.get_or_insert(f.ident);
|
|
|
|
|
for _ in 0..f_count {
|
|
|
|
|
attrs.extend(f_attr.clone());
|
|
|
|
|
}
|
|
|
|
|
count += f_count;
|
|
|
|
|
} else {
|
|
|
|
|
assert!(
|
|
|
|
|
f_attr.is_none(),
|
|
|
|
|
"{attr_name} attribute is only allowed on {slice_type}"
|
|
|
|
|
);
|
2025-10-04 15:17:15 +02:00
|
|
|
if first.is_some() {
|
2024-07-31 12:20:26 -05:00
|
|
|
found_last = true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
panic!("Fields are not named");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let slice_type = Ident::new(slice_type, Span::call_site());
|
|
|
|
|
let attr_type = Ident::new(attr_type, Span::call_site());
|
|
|
|
|
if let Some(first) = first {
|
|
|
|
|
quote! {
|
|
|
|
|
impl compiler::as_slice::AsSlice<#slice_type> for #ident {
|
|
|
|
|
type Attr = #attr_type;
|
|
|
|
|
|
|
|
|
|
fn as_slice(&self) -> &[#slice_type] {
|
|
|
|
|
unsafe {
|
|
|
|
|
let first = &self.#first as *const #slice_type;
|
|
|
|
|
std::slice::from_raw_parts(first, #count)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn as_mut_slice(&mut self) -> &mut [#slice_type] {
|
|
|
|
|
unsafe {
|
|
|
|
|
let first =
|
|
|
|
|
&mut self.#first as *mut #slice_type;
|
|
|
|
|
std::slice::from_raw_parts_mut(first, #count)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn attrs(&self) -> AttrList<Self::Attr> {
|
|
|
|
|
static ATTRS: [#attr_type; #count] = [#attrs];
|
|
|
|
|
AttrList::Array(&ATTRS)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
quote! {
|
|
|
|
|
impl compiler::as_slice::AsSlice<#slice_type> for #ident {
|
|
|
|
|
type Attr = #attr_type;
|
|
|
|
|
|
|
|
|
|
fn as_slice(&self) -> &[#slice_type] {
|
|
|
|
|
&[]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn as_mut_slice(&mut self) -> &mut [#slice_type] {
|
|
|
|
|
&mut []
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn attrs(&self) -> AttrList<Self::Attr> {
|
|
|
|
|
AttrList::Uniform(#attr_type::DEFAULT)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.into()
|
|
|
|
|
}
|
|
|
|
|
Data::Enum(e) => {
|
|
|
|
|
let mut as_slice_cases = TokenStream2::new();
|
|
|
|
|
let mut as_mut_slice_cases = TokenStream2::new();
|
|
|
|
|
let mut types_cases = TokenStream2::new();
|
|
|
|
|
let slice_type = Ident::new(slice_type, Span::call_site());
|
|
|
|
|
let attr_type = Ident::new(attr_type, Span::call_site());
|
|
|
|
|
for v in e.variants {
|
|
|
|
|
let case = v.ident;
|
|
|
|
|
as_slice_cases.extend(quote! {
|
|
|
|
|
#ident::#case(x) => compiler::as_slice::AsSlice::<#slice_type>::as_slice(x),
|
|
|
|
|
});
|
|
|
|
|
as_mut_slice_cases.extend(quote! {
|
|
|
|
|
#ident::#case(x) => compiler::as_slice::AsSlice::<#slice_type>::as_mut_slice(x),
|
|
|
|
|
});
|
|
|
|
|
types_cases.extend(quote! {
|
|
|
|
|
#ident::#case(x) => compiler::as_slice::AsSlice::<#slice_type>::attrs(x),
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
quote! {
|
|
|
|
|
impl compiler::as_slice::AsSlice<#slice_type> for #ident {
|
|
|
|
|
type Attr = #attr_type;
|
|
|
|
|
|
|
|
|
|
fn as_slice(&self) -> &[#slice_type] {
|
|
|
|
|
match self {
|
|
|
|
|
#as_slice_cases
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn as_mut_slice(&mut self) -> &mut [#slice_type] {
|
|
|
|
|
match self {
|
|
|
|
|
#as_mut_slice_cases
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn attrs(&self) -> AttrList<Self::Attr> {
|
|
|
|
|
match self {
|
|
|
|
|
#types_cases
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.into()
|
|
|
|
|
}
|
|
|
|
|
_ => panic!("Not a struct type"),
|
|
|
|
|
}
|
|
|
|
|
}
|