Skip to content

Commit

Permalink
Merge pull request #44 from sonos/impl-creprof-cdrop-and-asrust-for-a…
Browse files Browse the repository at this point in the history
…rrays

impl CReprOf CDrop and AsRust for arrays
  • Loading branch information
fredszaq authored Jan 18, 2023
2 parents edcf5d6 + 49825d6 commit 31511c3
Show file tree
Hide file tree
Showing 8 changed files with 189 additions and 51 deletions.
23 changes: 17 additions & 6 deletions ffi-convert-derive/src/asrust.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use proc_macro::TokenStream;
use quote::quote;
use syn::parse::{Parse, ParseBuffer};

use crate::utils::{parse_struct_fields, parse_target_type, Field};
use crate::utils::{parse_struct_fields, parse_target_type, Field, TypeArrayOrTypePath};

pub fn impl_asrust_macro(input: &syn::DeriveInput) -> TokenStream {
let struct_name = &input.ident;
Expand All @@ -20,12 +20,12 @@ pub fn impl_asrust_macro(input: &syn::DeriveInput) -> TokenStream {
} = field;

if field.levels_of_indirection > 1 && !field.is_nullable {
panic!(format!(
panic!(
"The CReprOf, AsRust, and CDrop traits cannot be derived automatically: \
The field {} is a pointer field has too many levels of indirection \
({} in this case). Please implements those traits manually.",
field_name, field.levels_of_indirection
))
)
}

let mut conversion = if field.is_string {
Expand All @@ -34,12 +34,23 @@ pub fn impl_asrust_macro(input: &syn::DeriveInput) -> TokenStream {
unsafe { std::ffi::CStr::raw_borrow(self.#field_name) }?.as_rust()?
})
} else if field.is_pointer {
quote!( {
let ref_to_struct = unsafe { #field_type::raw_borrow(self.#field_name)? };
match field_type {
TypeArrayOrTypePath::TypeArray(type_array) => {
quote!( {
let ref_to_array = unsafe { <#type_array>::raw_borrow(self.#field_name)? };
let converted_array = ref_to_struct.as_rust()?;
converted_array
})
}
TypeArrayOrTypePath::TypePath(type_path) => {
quote!( {
let ref_to_struct = unsafe { #type_path::raw_borrow(self.#field_name)? };
let converted_struct = ref_to_struct.as_rust()?;
converted_struct
})
}
)
}

} else {
quote!(self.#field_name.as_rust()?)
};
Expand Down
11 changes: 9 additions & 2 deletions ffi-convert-derive/src/cdrop.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::utils::{parse_no_drop_impl_flag, parse_struct_fields, Field};
use crate::utils::{parse_no_drop_impl_flag, parse_struct_fields, Field, TypeArrayOrTypePath};
use proc_macro::TokenStream;
use quote::quote;

Expand All @@ -23,7 +23,14 @@ pub fn impl_cdrop_macro(input: &syn::DeriveInput) -> TokenStream {
unsafe { std::ffi::CString::drop_raw_pointer(self.#field_name) }?
})
} else if field.is_pointer {
quote!( unsafe { #field_type::drop_raw_pointer(self.#field_name) }? )
match field_type {
TypeArrayOrTypePath::TypeArray(type_array) => {
quote!( unsafe { <#type_array>::drop_raw_pointer(self.#field_name) }? )
}
TypeArrayOrTypePath::TypePath(type_path) => {
quote!( unsafe { #type_path::drop_raw_pointer(self.#field_name) }? )
}
}
} else {
// the other cases will be handled automatically by rust
quote!()
Expand Down
11 changes: 9 additions & 2 deletions ffi-convert-derive/src/creprof.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use proc_macro::TokenStream;

use quote::quote;

use crate::utils::{parse_struct_fields, parse_target_type, Field};
use crate::utils::{parse_struct_fields, parse_target_type, Field, TypeArrayOrTypePath};

pub fn impl_creprof_macro(input: &syn::DeriveInput) -> TokenStream {
let struct_name = &input.ident;
Expand All @@ -22,7 +22,14 @@ pub fn impl_creprof_macro(input: &syn::DeriveInput) -> TokenStream {
let mut conversion = if field.is_string {
quote!(std::ffi::CString::c_repr_of(field)?)
} else {
quote!(#field_type::c_repr_of(field)?)
match field_type {
TypeArrayOrTypePath::TypeArray(type_array) => {
quote!(<#type_array>::c_repr_of(field)?)
}
TypeArrayOrTypePath::TypePath(type_path) => {
quote!(#type_path::c_repr_of(field)?)
}
}
};

if field.is_pointer {
Expand Down
93 changes: 59 additions & 34 deletions ffi-convert-derive/src/utils.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
use syn::parse::{Parse, ParseBuffer};

pub fn parse_target_type(attrs: &[syn::Attribute]) -> syn::Path {
let target_type_attribute = attrs
.iter()
Expand Down Expand Up @@ -28,32 +26,16 @@ pub fn parse_struct_fields(data: &syn::Data) -> Vec<Field> {
}
}

struct CReprOfConvertOverrideArgs {
pub convert: syn::Expr,
}

impl<'a> Parse for CReprOfConvertOverrideArgs {
fn parse(input: &ParseBuffer) -> Result<Self, syn::parse::Error> {
let convert = input.parse()?;
Ok(Self { convert })
}
}

struct TargetNameArgs {
pub name: syn::Ident,
}

impl<'a> Parse for TargetNameArgs {
fn parse(input: &ParseBuffer) -> Result<Self, syn::parse::Error> {
let name = input.parse()?;
Ok(Self { name })
}
#[derive(PartialEq, Eq, Debug)]
pub enum TypeArrayOrTypePath {
TypeArray(syn::TypeArray),
TypePath(syn::TypePath),
}

pub struct Field<'a> {
pub name: &'a syn::Ident,
pub target_name: syn::Ident,
pub field_type: syn::TypePath,
pub field_type: TypeArrayOrTypePath,
pub type_params: Option<syn::AngleBracketedGenericArguments>,
pub is_nullable: bool,
pub is_string: bool,
Expand Down Expand Up @@ -85,6 +67,7 @@ pub fn parse_field(field: &syn::Field) -> Field {

let (field_type, type_params) = match inner_field_type {
syn::Type::Path(type_path) => generic_path_to_concrete_type_path(type_path),
syn::Type::Array(type_array) => (TypeArrayOrTypePath::TypeArray(type_array), None),
_ => panic!("Field type used in this struct is not supported by the proc macro"),
};

Expand Down Expand Up @@ -145,7 +128,10 @@ pub fn parse_field(field: &syn::Field) -> Field {
///
pub fn generic_path_to_concrete_type_path(
mut path: syn::TypePath,
) -> (syn::TypePath, Option<syn::AngleBracketedGenericArguments>) {
) -> (
TypeArrayOrTypePath,
Option<syn::AngleBracketedGenericArguments>,
) {
let last_seg: Option<&mut syn::PathSegment> = path.path.segments.last_mut();

if let Some(last_segment) = last_seg {
Expand All @@ -154,9 +140,12 @@ pub fn generic_path_to_concrete_type_path(
{
let extracted_type_params = (*bracketed_type_params).clone();
last_segment.arguments = syn::PathArguments::None;
(path, Some(extracted_type_params))
(
TypeArrayOrTypePath::TypePath(path),
Some(extracted_type_params),
)
} else {
(path, None)
(TypeArrayOrTypePath::TypePath(path), None)
}
} else {
panic!("Invalid type path: no segments on the TypePath")
Expand All @@ -179,7 +168,9 @@ mod tests {
assert_eq!(extracted_type_param.unwrap().args.len(), 1);
assert_eq!(
transformed_type_path,
syn::parse_str::<TypePath>("std::mod1::mod2::Foo").unwrap()
TypeArrayOrTypePath::TypePath(
syn::parse_str::<TypePath>("std::mod1::mod2::Foo").unwrap()
)
);
}

Expand All @@ -192,7 +183,9 @@ mod tests {

assert_eq!(
transformed_type_path,
syn::parse_str::<TypePath>("std::mod1::mod2::Foo").unwrap()
TypeArrayOrTypePath::TypePath(
syn::parse_str::<TypePath>("std::mod1::mod2::Foo").unwrap()
)
);
assert_eq!(extracted_type_param.unwrap().args.len(), 2)
}
Expand All @@ -207,7 +200,9 @@ mod tests {
assert!(extracted_type_params.is_none());
assert_eq!(
transformed_path,
syn::parse_str::<TypePath>("std::module1::module2::Hello").unwrap()
TypeArrayOrTypePath::TypePath(
syn::parse_str::<TypePath>("std::module1::module2::Hello").unwrap()
)
)
}

Expand All @@ -221,7 +216,11 @@ mod tests {
assert_eq!(parsed_fields[0].is_pointer, true);
assert_eq!(parsed_fields[0].is_nullable, false);

assert_eq!(parsed_fields[0].field_type.path.segments.len(), 2);
if let TypeArrayOrTypePath::TypePath(type_path) = &parsed_fields[0].field_type {
assert_eq!(type_path.path.segments.len(), 2);
} else {
panic!("Unexpected type")
}
}

#[test]
Expand Down Expand Up @@ -249,8 +248,21 @@ mod tests {
assert_eq!(parsed_fields[0].is_string, false);
assert_eq!(parsed_fields[1].is_string, false);

let parsed_path_0 = parsed_fields[0].field_type.path.clone();
let parsed_path_1 = parsed_fields[1].field_type.path.clone();
let field_type0 =
if let TypeArrayOrTypePath::TypePath(type_path) = &parsed_fields[0].field_type {
type_path
} else {
panic!("unexpected type")
};
let field_type1 =
if let TypeArrayOrTypePath::TypePath(type_path) = &parsed_fields[1].field_type {
type_path
} else {
panic!("unexpected type")
};

let parsed_path_0 = field_type0.path.clone();
let parsed_path_1 = field_type1.path.clone();

assert_eq!(parsed_path_0.segments.len(), 2);
assert_eq!(parsed_path_1.segments.len(), 1);
Expand Down Expand Up @@ -281,8 +293,21 @@ mod tests {
assert_eq!(parsed_fields[0].is_string, false);
assert_eq!(parsed_fields[1].is_string, false);

let parsed_path_0 = parsed_fields[0].field_type.path.clone();
let parsed_path_1 = parsed_fields[1].field_type.path.clone();
let field_type0 =
if let TypeArrayOrTypePath::TypePath(type_path) = &parsed_fields[0].field_type {
type_path
} else {
panic!("unexpected type")
};
let field_type1 =
if let TypeArrayOrTypePath::TypePath(type_path) = &parsed_fields[1].field_type {
type_path
} else {
panic!("unexpected type")
};

let parsed_path_0 = field_type0.path.clone();
let parsed_path_1 = field_type1.path.clone();

assert_eq!(parsed_path_0.segments.len(), 2);
assert_eq!(parsed_path_1.segments.len(), 1);
Expand Down
40 changes: 37 additions & 3 deletions ffi-convert-tests/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,12 @@ pub struct Pancake {
pub description: Option<String>,
pub start: f32,
pub end: Option<f32>,
pub float_array: [f32; 4],
pub dummy: Dummy,
pub sauce: Option<Sauce>,
pub toppings: Vec<Topping>,
pub layers: Option<Vec<Layer>>,
pub base_layers: [Layer; 3],
pub is_delicious: bool,
pub range: Range<usize>,
pub some_futile_info: Option<String>,
Expand All @@ -61,12 +63,14 @@ pub struct CPancake {
start: f32,
#[nullable]
end: *const f32,
float_array: [f32; 4],
dummy: CDummy,
#[nullable]
sauce: *const CSauce,
toppings: *const CArray<CTopping>,
#[nullable]
layers: *const CArray<CLayer>,
base_layers: [CLayer; 3],
is_delicious: bool,
pub range: CRange<i32>,
#[c_repr_of_convert(input.flattened_range.start)]
Expand All @@ -91,7 +95,7 @@ pub struct CSauce {
volume: f32,
}

#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Topping {
pub amount: i32,
}
Expand All @@ -103,7 +107,7 @@ pub struct CTopping {
amount: i32,
}

#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Layer {
pub number: i32,
pub subtitle: Option<String>,
Expand All @@ -118,7 +122,7 @@ pub struct CLayer {
subtitle: *const libc::c_char,
}

#[derive(Clone, Debug, PartialEq)]
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Dummy {
pub count: i32,
pub describe: String,
Expand Down Expand Up @@ -162,6 +166,7 @@ mod tests {
description: Some("I'm delicious ! ".to_string()),
start: 0.0,
end: Some(2.0),
float_array: [1.0, 2.0, 3.0, 4.0],
dummy: Dummy {
count: 2,
describe: "yo".to_string(),
Expand All @@ -172,6 +177,20 @@ mod tests {
number: 1,
subtitle: Some(String::from("first layer")),
}]),
base_layers: [
Layer {
number: 0,
subtitle: Some(String::from("flour")),
},
Layer {
number: 1,
subtitle: Some(String::from("dough")),
},
Layer {
number: 2,
subtitle: Some(String::from("tomato")),
},
],
is_delicious: true,
range: Range { start: 20, end: 30 },
some_futile_info: None,
Expand All @@ -187,13 +206,28 @@ mod tests {
description: Some("I'm delicious ! ".to_string()),
start: 0.0,
end: None,
float_array: [8.0, -1.0, f32::INFINITY, -0.0],
dummy: Dummy {
count: 2,
describe: "yo".to_string(),
},
sauce: None,
toppings: vec![],
layers: Some(vec![]),
base_layers: [
Layer {
number: 0,
subtitle: Some(String::from("flour")),
},
Layer {
number: 1,
subtitle: Some(String::from("dough")),
},
Layer {
number: 2,
subtitle: Some(String::from("cream")),
},
],
is_delicious: true,
range: Range {
start: 50,
Expand Down
2 changes: 1 addition & 1 deletion ffi-convert/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
name = "ffi-convert"
version = "0.6.0-pre"
authors = ["Sonos"]
edition = "2018"
edition = "2021"
license = "MIT OR Apache-2.0"
description = "A collection of utilities to ease conversion between Rust and C-compatible data structures."
repository = "https://github.com/sonos/ffi-convert-rs"
Expand Down
Loading

0 comments on commit 31511c3

Please sign in to comment.