Loading tools/pdl/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ rust_binary_host { filegroup { name: "pdl_generated_files", srcs: [ "tests/generated/custom_field_declaration_big_endian.rs", "tests/generated/custom_field_declaration_little_endian.rs", "tests/generated/enum_declaration_big_endian.rs", "tests/generated/enum_declaration_little_endian.rs", "tests/generated/packet_decl_8bit_enum_array_big_endian.rs", Loading Loading @@ -75,10 +77,14 @@ filegroup { "tests/generated/packet_decl_array_unknown_element_width_dynamic_count_little_endian.rs", "tests/generated/packet_decl_array_unknown_element_width_dynamic_size_big_endian.rs", "tests/generated/packet_decl_array_unknown_element_width_dynamic_size_little_endian.rs", "tests/generated/packet_decl_array_with_padding_big_endian.rs", "tests/generated/packet_decl_array_with_padding_little_endian.rs", "tests/generated/packet_decl_child_packets_big_endian.rs", "tests/generated/packet_decl_child_packets_little_endian.rs", "tests/generated/packet_decl_complex_scalars_big_endian.rs", "tests/generated/packet_decl_complex_scalars_little_endian.rs", "tests/generated/packet_decl_custom_field_big_endian.rs", "tests/generated/packet_decl_custom_field_little_endian.rs", "tests/generated/packet_decl_empty_big_endian.rs", "tests/generated/packet_decl_empty_little_endian.rs", "tests/generated/packet_decl_fixed_enum_field_big_endian.rs", Loading tools/pdl/src/analyzer.rs +47 −11 Original line number Diff line number Diff line Loading @@ -54,6 +54,9 @@ pub mod ast { pub struct FieldAnnotation { // Size of field. pub size: Size, // Size of field with padding bytes. // This information exists only for array fields. pub padded_size: Option<usize>, } #[derive(Default, Debug, Clone)] Loading @@ -65,6 +68,12 @@ pub mod ast { pub payload_size: Size, } impl FieldAnnotation { pub fn new(size: Size) -> Self { FieldAnnotation { size, padded_size: None } } } impl std::ops::Add for Size { type Output = Size; fn add(self, rhs: Size) -> Self::Output { Loading Loading @@ -1320,7 +1329,7 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { ) -> ast::Field { field.annotate(match &field.desc { FieldDesc::Checksum { .. } | FieldDesc::Padding { .. } => { ast::FieldAnnotation { size: ast::Size::Static(0) } ast::FieldAnnotation::new(ast::Size::Static(0)) } FieldDesc::Size { width, .. } | FieldDesc::Count { width, .. } Loading @@ -1328,7 +1337,7 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { | FieldDesc::FixedScalar { width, .. } | FieldDesc::Reserved { width } | FieldDesc::Scalar { width, .. } => { ast::FieldAnnotation { size: ast::Size::Static(*width) } ast::FieldAnnotation::new(ast::Size::Static(*width)) } FieldDesc::Body | FieldDesc::Payload { .. } => { let has_payload_size = decl.fields().any(|field| match &field.desc { Loading @@ -1337,22 +1346,24 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { } _ => false, }); ast::FieldAnnotation { size: if has_payload_size { ast::Size::Dynamic } else { ast::Size::Unknown }, } ast::FieldAnnotation::new(if has_payload_size { ast::Size::Dynamic } else { ast::Size::Unknown }) } FieldDesc::Typedef { type_id, .. } | FieldDesc::FixedEnum { enum_id: type_id, .. } | FieldDesc::Group { group_id: type_id, .. } => { let type_annot = scope.get(type_id).unwrap(); ast::FieldAnnotation { size: type_annot.size + type_annot.payload_size } ast::FieldAnnotation::new(type_annot.size + type_annot.payload_size) } FieldDesc::Array { width: Some(width), size: Some(size), .. } => { ast::FieldAnnotation { size: ast::Size::Static(*size * *width) } ast::FieldAnnotation::new(ast::Size::Static(*size * *width)) } FieldDesc::Array { width: None, size: Some(size), type_id: Some(type_id), .. } => { let type_annot = scope.get(type_id).unwrap(); ast::FieldAnnotation { size: (type_annot.size + type_annot.payload_size) * *size } ast::FieldAnnotation::new((type_annot.size + type_annot.payload_size) * *size) } FieldDesc::Array { id, size: None, .. } => { // The element does not matter when the size of the array is Loading @@ -1364,9 +1375,11 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { } _ => false, }); ast::FieldAnnotation { size: if has_array_size { ast::Size::Dynamic } else { ast::Size::Unknown }, } ast::FieldAnnotation::new(if has_array_size { ast::Size::Dynamic } else { ast::Size::Unknown }) } FieldDesc::Array { .. } => unreachable!(), }) Loading Loading @@ -1394,6 +1407,28 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { } } /// Inline padding fields. /// The padding information is added directly to the targeted fields. fn inline_paddings(file: &mut ast::File) { for decl in file.declarations.iter_mut() { match &mut decl.desc { DeclDesc::Struct { fields, .. } | DeclDesc::Packet { fields, .. } | DeclDesc::Group { fields, .. } => { let mut padding = None; for field in fields.iter_mut().rev() { field.annot.padded_size = padding; padding = match &field.desc { FieldDesc::Padding { size } => Some(*size), _ => None, }; } } _ => (), } } } /// Inline group fields and remove group declarations. fn inline_groups(file: &mut ast::File) -> Result<(), Diagnostics> { fn inline_fields<'a>( Loading Loading @@ -1474,6 +1509,7 @@ pub fn analyze(file: &parser_ast::File) -> Result<ast::File, Diagnostics> { check_padding_fields(file)?; check_checksum_fields(file, &scope)?; let mut file = compute_field_sizes(file); inline_paddings(&mut file); inline_groups(&mut file)?; Ok(file) } Loading tools/pdl/src/backends/rust.rs +106 −0 Original line number Diff line number Diff line Loading @@ -899,6 +899,67 @@ fn generate_enum_decl( } } /// Generate the declaration for a custom field of static size. /// /// * `id` - Enum identifier. /// * `width` - Width of the backing type of the enum, in bits. fn generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStream { let id = format_ident!("{}", id); let backing_type = types::Integer::new(width); let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width)); let max_value = mask_bits(width, "usize"); let common = quote! { impl From<&#id> for #backing_type { fn from(value: &#id) -> #backing_type { value.0 } } impl From<#id> for #backing_type { fn from(value: #id) -> #backing_type { value.0 } } }; if backing_type.width == width { quote! { #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(from = #backing_type_str, into = #backing_type_str))] pub struct #id(#backing_type); #common impl From<#backing_type> for #id { fn from(value: #backing_type) -> Self { #id(value) } } } } else { quote! { #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = #backing_type_str, into = #backing_type_str))] pub struct #id(#backing_type); #common impl TryFrom<#backing_type> for #id { type Error = #backing_type; fn try_from(value: #backing_type) -> std::result::Result<Self, Self::Error> { if value > #max_value { Err(value) } else { Ok(#id(value)) } } } } } } fn generate_decl( scope: &lint::Scope<'_>, file: &analyzer_ast::File, Loading @@ -919,6 +980,9 @@ fn generate_decl( ast::DeclDesc::Enum { id, tags, width } => { generate_enum_decl(id, tags, *width, false).to_string() } ast::DeclDesc::CustomField { id, width: Some(width), .. } => { generate_custom_field_decl(id, *width).to_string() } _ => todo!("unsupported Decl::{:?}", decl), } } Loading Loading @@ -1104,6 +1168,20 @@ mod tests { "# ); test_pdl!( custom_field_declaration, r#" // Still unsupported. // custom_field Dynamic "dynamic" // Should generate a type with From<u32> implementation. custom_field ExactSize : 32 "exact_size" // Should generate a type with TryFrom<u32> implementation. custom_field TruncatedSize : 24 "truncated_size" "# ); test_pdl!( packet_decl_simple_scalars, r#" Loading Loading @@ -1251,6 +1329,21 @@ mod tests { " ); test_pdl!( packet_decl_array_with_padding, " struct Foo { _count_(a): 40, a: 16[], } packet Bar { a: Foo[], _padding_ [128], } " ); test_pdl!( packet_decl_reserved_field, " Loading @@ -1260,6 +1353,19 @@ mod tests { " ); test_pdl!( packet_decl_custom_field, r#" custom_field Bar1 : 24 "exact" custom_field Bar2 : 32 "truncated" packet Foo { a: Bar1, b: Bar2, } "# ); test_pdl!( packet_decl_fixed_scalar_field, " Loading tools/pdl/src/backends/rust/parser.rs +54 −29 Original line number Diff line number Diff line Loading @@ -63,12 +63,13 @@ impl<'a> FieldParser<'a> { pub fn add(&mut self, field: &'a analyzer_ast::Field) { match &field.desc { _ if self.scope.is_bitfield(field) => self.add_bit_field(field), ast::FieldDesc::Padding { .. } => todo!("Padding fields are not supported"), ast::FieldDesc::Padding { .. } => (), ast::FieldDesc::Array { id, width, type_id, size, .. } => self.add_array_field( id, *width, type_id.as_deref(), *size, field.annot.padded_size, self.scope.get_field_declaration(field), ), ast::FieldDesc::Typedef { id, type_id } => self.add_typedef_field(id, type_id), Loading @@ -91,7 +92,7 @@ impl<'a> FieldParser<'a> { let end_offset = self.offset + size; let wanted = proc_macro2::Literal::usize_unsuffixed(size); self.check_size("e!(#wanted)); self.check_size(self.span, "e!(#wanted)); let chunk_type = types::Integer::new(self.shift); // TODO(mgeisler): generate Rust variable names which cannot Loading Loading @@ -252,9 +253,8 @@ impl<'a> FieldParser<'a> { Some(offset) } fn check_size(&mut self, wanted: &proc_macro2::TokenStream) { fn check_size(&mut self, span: &proc_macro2::Ident, wanted: &proc_macro2::TokenStream) { let packet_name = &self.packet_name; let span = self.span; self.code.push(quote! { if #span.get().remaining() < #wanted { return Err(Error::InvalidLengthError { Loading @@ -277,6 +277,7 @@ impl<'a> FieldParser<'a> { // `size`: the size of the array in number of elements (if // known). If None, the array is a Vec with a dynamic size. size: Option<usize>, padding_size: Option<usize>, decl: Option<&analyzer_ast::Decl>, ) { enum ElementWidth { Loading Loading @@ -312,18 +313,29 @@ impl<'a> FieldParser<'a> { // TODO size modifier // TODO padded_size let span = match padding_size { Some(padding_size) => { let span = self.span; self.check_size(span, "e!(#padding_size)); self.code.push(quote! { let (head, tail) = #span.get().split_at(#padding_size); let mut head = &mut Cell::new(head); #span.replace(tail); }); format_ident!("head") } None => self.span.clone(), }; let id = format_ident!("{id}"); let span = self.span; let parse_element = self.parse_array_element(self.span, width, type_id, decl); let parse_element = self.parse_array_element(&span, width, type_id, decl); match (element_width, &array_shape) { (ElementWidth::Unknown, ArrayShape::SizeField(size_field)) => { // The element width is not known, but the array full // octet size is known by size field. Parse elements // item by item as a vector. self.check_size("e!(#size_field)); self.check_size(&span, "e!(#size_field)); let parse_element = self.parse_array_element(&format_ident!("head"), width, type_id, decl); self.code.push(quote! { Loading Loading @@ -383,7 +395,7 @@ impl<'a> FieldParser<'a> { let element_width = syn::Index::from(element_width); quote!(#count * #element_width) }; self.check_size(&array_size); self.check_size(&span, "e! { #array_size }); self.code.push(quote! { // TODO(mgeisler): use // https://doc.rust-lang.org/std/array/fn.try_from_fn.html Loading @@ -395,10 +407,10 @@ impl<'a> FieldParser<'a> { .map_err(|_| Error::InvalidPacketError)?; }); } (ElementWidth::Static(_), ArrayShape::CountField(count_field)) => { (ElementWidth::Static(element_width), ArrayShape::CountField(count_field)) => { // The element width is known, and the array element // count is known dynamically by the count field. self.check_size("e!(#count_field)); self.check_size(&span, "e!(#count_field * #element_width)); self.code.push(quote! { let #id = (0..#count_field) .map(|_| #parse_element) Loading @@ -411,7 +423,7 @@ impl<'a> FieldParser<'a> { // is known by size field, or unknown (in which case // it is the remaining span length). let array_size = if let ArrayShape::SizeField(size_field) = &array_shape { self.check_size("e!(#size_field)); self.check_size(&span, "e!(#size_field)); quote!(#size_field) } else { quote!(#span.get().remaining()) Loading Loading @@ -459,28 +471,41 @@ impl<'a> FieldParser<'a> { let id = format_ident!("{id}"); let type_id = format_ident!("{type_id}"); match self.scope.get_decl_width(decl, true) { None => self.code.push(quote! { self.code.push(match self.scope.get_decl_width(decl, true) { None => quote! { let #id = #type_id::parse_inner(&mut #span)?; }), }, Some(width) => { assert_eq!(width % 8, 0, "Typedef field type size is not a multiple of 8"); let width = syn::Index::from(width / 8); self.code.push(if let ast::DeclDesc::Checksum { .. } = &decl.desc { // TODO: handle checksum fields. match &decl.desc { ast::DeclDesc::Checksum { .. } => todo!(), ast::DeclDesc::CustomField { .. } if [8, 16, 32, 64].contains(&width) => { let get_uint = types::get_uint(self.endianness, width, span); quote! { #span.get_mut().advance(#width); let #id = #get_uint.into(); } } else { } ast::DeclDesc::CustomField { .. } => { let get_uint = types::get_uint(self.endianness, width, span); quote! { let #id = (#get_uint) .try_into() .unwrap(); // Value is masked and conversion must succeed. } } ast::DeclDesc::Struct { .. } => { let width = syn::Index::from(width / 8); quote! { let (head, tail) = #span.get().split_at(#width); #span.replace(tail); let #id = #type_id::parse(head)?; } }); } _ => unreachable!(), } } }); } /// Parse body and payload fields. fn add_payload_field(&mut self, size_modifier: Option<&str>) { Loading Loading @@ -518,7 +543,7 @@ impl<'a> FieldParser<'a> { // payload and update the span in case fields are placed // after the payload. let size_field = size_field_ident(field_id); self.check_size("e!(#size_field )); self.check_size(self.span, "e!(#size_field )); self.code.push(quote! { let payload = &#span.get()[..#size_field]; #span.get_mut().advance(#size_field); Loading @@ -540,7 +565,7 @@ impl<'a> FieldParser<'a> { "Payload field offset from end of packet is not a multiple of 8" ); let offset_from_end = syn::Index::from(offset_from_end / 8); self.check_size("e!(#offset_from_end)); self.check_size(self.span, "e!(#offset_from_end)); self.code.push(quote! { let payload = &#span.get()[..#span.get().len() - #offset_from_end]; #span.get_mut().advance(payload.len()); Loading tools/pdl/src/backends/rust/serializer.rs +49 −12 Original line number Diff line number Diff line Loading @@ -55,15 +55,20 @@ impl<'a> FieldSerializer<'a> { pub fn add(&mut self, field: &analyzer_ast::Field) { match &field.desc { _ if self.scope.is_bitfield(field) => self.add_bit_field(field), ast::FieldDesc::Array { id, width, .. } => { self.add_array_field(id, *width, self.scope.get_field_declaration(field)) } ast::FieldDesc::Array { id, width, .. } => self.add_array_field( id, *width, field.annot.padded_size, self.scope.get_field_declaration(field), ), ast::FieldDesc::Typedef { id, type_id } => { self.add_typedef_field(id, type_id); } ast::FieldDesc::Payload { .. } | ast::FieldDesc::Body { .. } => { self.add_payload_field() } // Padding field handled in serialization of associated array field. ast::FieldDesc::Padding { .. } => (), _ => todo!("Cannot yet serialize {field:?}"), } } Loading Loading @@ -258,10 +263,10 @@ impl<'a> FieldSerializer<'a> { &mut self, id: &str, width: Option<usize>, padding_size: Option<usize>, decl: Option<&analyzer_ast::Decl>, ) { // TODO: padding let span = format_ident!("{}", self.span); let serialize = match width { Some(width) => { let value = quote!(*elem); Loading @@ -277,7 +282,6 @@ impl<'a> FieldSerializer<'a> { self.span, ) } else { let span = format_ident!("{}", self.span); quote! { elem.write_to(#span) } Loading @@ -286,10 +290,26 @@ impl<'a> FieldSerializer<'a> { }; let id = format_ident!("{id}"); self.code.push(quote! { self.code.push(match padding_size { Some(padding_size) => quote! { let current_size = #span.len(); for elem in &self.#id { #serialize; } let array_size = #span.len() - current_size; if array_size > #padding_size { panic!("attempted to serialize an array larger than the enclosing padding size"); } #span.put_bytes(0, #padding_size - array_size); }, None => quote! { for elem in &self.#id { #serialize; } } }); } Loading @@ -302,8 +322,25 @@ impl<'a> FieldSerializer<'a> { let id = format_ident!("{id}"); let span = format_ident!("{}", self.span); self.code.push(quote! { self.code.push(match &decl.desc { ast::DeclDesc::Checksum { .. } => todo!(), ast::DeclDesc::CustomField { width: Some(width), .. } => { let backing_type = types::Integer::new(*width); let put_uint = types::put_uint( self.endianness, "e! { #backing_type::from(self.#id) }, *width, self.span, ); quote! { #put_uint; } } ast::DeclDesc::Struct { .. } => quote! { self.#id.write_to(#span); }, _ => unreachable!(), }); } Loading Loading
tools/pdl/Android.bp +6 −0 Original line number Diff line number Diff line Loading @@ -41,6 +41,8 @@ rust_binary_host { filegroup { name: "pdl_generated_files", srcs: [ "tests/generated/custom_field_declaration_big_endian.rs", "tests/generated/custom_field_declaration_little_endian.rs", "tests/generated/enum_declaration_big_endian.rs", "tests/generated/enum_declaration_little_endian.rs", "tests/generated/packet_decl_8bit_enum_array_big_endian.rs", Loading Loading @@ -75,10 +77,14 @@ filegroup { "tests/generated/packet_decl_array_unknown_element_width_dynamic_count_little_endian.rs", "tests/generated/packet_decl_array_unknown_element_width_dynamic_size_big_endian.rs", "tests/generated/packet_decl_array_unknown_element_width_dynamic_size_little_endian.rs", "tests/generated/packet_decl_array_with_padding_big_endian.rs", "tests/generated/packet_decl_array_with_padding_little_endian.rs", "tests/generated/packet_decl_child_packets_big_endian.rs", "tests/generated/packet_decl_child_packets_little_endian.rs", "tests/generated/packet_decl_complex_scalars_big_endian.rs", "tests/generated/packet_decl_complex_scalars_little_endian.rs", "tests/generated/packet_decl_custom_field_big_endian.rs", "tests/generated/packet_decl_custom_field_little_endian.rs", "tests/generated/packet_decl_empty_big_endian.rs", "tests/generated/packet_decl_empty_little_endian.rs", "tests/generated/packet_decl_fixed_enum_field_big_endian.rs", Loading
tools/pdl/src/analyzer.rs +47 −11 Original line number Diff line number Diff line Loading @@ -54,6 +54,9 @@ pub mod ast { pub struct FieldAnnotation { // Size of field. pub size: Size, // Size of field with padding bytes. // This information exists only for array fields. pub padded_size: Option<usize>, } #[derive(Default, Debug, Clone)] Loading @@ -65,6 +68,12 @@ pub mod ast { pub payload_size: Size, } impl FieldAnnotation { pub fn new(size: Size) -> Self { FieldAnnotation { size, padded_size: None } } } impl std::ops::Add for Size { type Output = Size; fn add(self, rhs: Size) -> Self::Output { Loading Loading @@ -1320,7 +1329,7 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { ) -> ast::Field { field.annotate(match &field.desc { FieldDesc::Checksum { .. } | FieldDesc::Padding { .. } => { ast::FieldAnnotation { size: ast::Size::Static(0) } ast::FieldAnnotation::new(ast::Size::Static(0)) } FieldDesc::Size { width, .. } | FieldDesc::Count { width, .. } Loading @@ -1328,7 +1337,7 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { | FieldDesc::FixedScalar { width, .. } | FieldDesc::Reserved { width } | FieldDesc::Scalar { width, .. } => { ast::FieldAnnotation { size: ast::Size::Static(*width) } ast::FieldAnnotation::new(ast::Size::Static(*width)) } FieldDesc::Body | FieldDesc::Payload { .. } => { let has_payload_size = decl.fields().any(|field| match &field.desc { Loading @@ -1337,22 +1346,24 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { } _ => false, }); ast::FieldAnnotation { size: if has_payload_size { ast::Size::Dynamic } else { ast::Size::Unknown }, } ast::FieldAnnotation::new(if has_payload_size { ast::Size::Dynamic } else { ast::Size::Unknown }) } FieldDesc::Typedef { type_id, .. } | FieldDesc::FixedEnum { enum_id: type_id, .. } | FieldDesc::Group { group_id: type_id, .. } => { let type_annot = scope.get(type_id).unwrap(); ast::FieldAnnotation { size: type_annot.size + type_annot.payload_size } ast::FieldAnnotation::new(type_annot.size + type_annot.payload_size) } FieldDesc::Array { width: Some(width), size: Some(size), .. } => { ast::FieldAnnotation { size: ast::Size::Static(*size * *width) } ast::FieldAnnotation::new(ast::Size::Static(*size * *width)) } FieldDesc::Array { width: None, size: Some(size), type_id: Some(type_id), .. } => { let type_annot = scope.get(type_id).unwrap(); ast::FieldAnnotation { size: (type_annot.size + type_annot.payload_size) * *size } ast::FieldAnnotation::new((type_annot.size + type_annot.payload_size) * *size) } FieldDesc::Array { id, size: None, .. } => { // The element does not matter when the size of the array is Loading @@ -1364,9 +1375,11 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { } _ => false, }); ast::FieldAnnotation { size: if has_array_size { ast::Size::Dynamic } else { ast::Size::Unknown }, } ast::FieldAnnotation::new(if has_array_size { ast::Size::Dynamic } else { ast::Size::Unknown }) } FieldDesc::Array { .. } => unreachable!(), }) Loading Loading @@ -1394,6 +1407,28 @@ fn compute_field_sizes(file: &parser_ast::File) -> ast::File { } } /// Inline padding fields. /// The padding information is added directly to the targeted fields. fn inline_paddings(file: &mut ast::File) { for decl in file.declarations.iter_mut() { match &mut decl.desc { DeclDesc::Struct { fields, .. } | DeclDesc::Packet { fields, .. } | DeclDesc::Group { fields, .. } => { let mut padding = None; for field in fields.iter_mut().rev() { field.annot.padded_size = padding; padding = match &field.desc { FieldDesc::Padding { size } => Some(*size), _ => None, }; } } _ => (), } } } /// Inline group fields and remove group declarations. fn inline_groups(file: &mut ast::File) -> Result<(), Diagnostics> { fn inline_fields<'a>( Loading Loading @@ -1474,6 +1509,7 @@ pub fn analyze(file: &parser_ast::File) -> Result<ast::File, Diagnostics> { check_padding_fields(file)?; check_checksum_fields(file, &scope)?; let mut file = compute_field_sizes(file); inline_paddings(&mut file); inline_groups(&mut file)?; Ok(file) } Loading
tools/pdl/src/backends/rust.rs +106 −0 Original line number Diff line number Diff line Loading @@ -899,6 +899,67 @@ fn generate_enum_decl( } } /// Generate the declaration for a custom field of static size. /// /// * `id` - Enum identifier. /// * `width` - Width of the backing type of the enum, in bits. fn generate_custom_field_decl(id: &str, width: usize) -> proc_macro2::TokenStream { let id = format_ident!("{}", id); let backing_type = types::Integer::new(width); let backing_type_str = proc_macro2::Literal::string(&format!("u{}", backing_type.width)); let max_value = mask_bits(width, "usize"); let common = quote! { impl From<&#id> for #backing_type { fn from(value: &#id) -> #backing_type { value.0 } } impl From<#id> for #backing_type { fn from(value: #id) -> #backing_type { value.0 } } }; if backing_type.width == width { quote! { #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(from = #backing_type_str, into = #backing_type_str))] pub struct #id(#backing_type); #common impl From<#backing_type> for #id { fn from(value: #backing_type) -> Self { #id(value) } } } } else { quote! { #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[cfg_attr(feature = "serde", serde(try_from = #backing_type_str, into = #backing_type_str))] pub struct #id(#backing_type); #common impl TryFrom<#backing_type> for #id { type Error = #backing_type; fn try_from(value: #backing_type) -> std::result::Result<Self, Self::Error> { if value > #max_value { Err(value) } else { Ok(#id(value)) } } } } } } fn generate_decl( scope: &lint::Scope<'_>, file: &analyzer_ast::File, Loading @@ -919,6 +980,9 @@ fn generate_decl( ast::DeclDesc::Enum { id, tags, width } => { generate_enum_decl(id, tags, *width, false).to_string() } ast::DeclDesc::CustomField { id, width: Some(width), .. } => { generate_custom_field_decl(id, *width).to_string() } _ => todo!("unsupported Decl::{:?}", decl), } } Loading Loading @@ -1104,6 +1168,20 @@ mod tests { "# ); test_pdl!( custom_field_declaration, r#" // Still unsupported. // custom_field Dynamic "dynamic" // Should generate a type with From<u32> implementation. custom_field ExactSize : 32 "exact_size" // Should generate a type with TryFrom<u32> implementation. custom_field TruncatedSize : 24 "truncated_size" "# ); test_pdl!( packet_decl_simple_scalars, r#" Loading Loading @@ -1251,6 +1329,21 @@ mod tests { " ); test_pdl!( packet_decl_array_with_padding, " struct Foo { _count_(a): 40, a: 16[], } packet Bar { a: Foo[], _padding_ [128], } " ); test_pdl!( packet_decl_reserved_field, " Loading @@ -1260,6 +1353,19 @@ mod tests { " ); test_pdl!( packet_decl_custom_field, r#" custom_field Bar1 : 24 "exact" custom_field Bar2 : 32 "truncated" packet Foo { a: Bar1, b: Bar2, } "# ); test_pdl!( packet_decl_fixed_scalar_field, " Loading
tools/pdl/src/backends/rust/parser.rs +54 −29 Original line number Diff line number Diff line Loading @@ -63,12 +63,13 @@ impl<'a> FieldParser<'a> { pub fn add(&mut self, field: &'a analyzer_ast::Field) { match &field.desc { _ if self.scope.is_bitfield(field) => self.add_bit_field(field), ast::FieldDesc::Padding { .. } => todo!("Padding fields are not supported"), ast::FieldDesc::Padding { .. } => (), ast::FieldDesc::Array { id, width, type_id, size, .. } => self.add_array_field( id, *width, type_id.as_deref(), *size, field.annot.padded_size, self.scope.get_field_declaration(field), ), ast::FieldDesc::Typedef { id, type_id } => self.add_typedef_field(id, type_id), Loading @@ -91,7 +92,7 @@ impl<'a> FieldParser<'a> { let end_offset = self.offset + size; let wanted = proc_macro2::Literal::usize_unsuffixed(size); self.check_size("e!(#wanted)); self.check_size(self.span, "e!(#wanted)); let chunk_type = types::Integer::new(self.shift); // TODO(mgeisler): generate Rust variable names which cannot Loading Loading @@ -252,9 +253,8 @@ impl<'a> FieldParser<'a> { Some(offset) } fn check_size(&mut self, wanted: &proc_macro2::TokenStream) { fn check_size(&mut self, span: &proc_macro2::Ident, wanted: &proc_macro2::TokenStream) { let packet_name = &self.packet_name; let span = self.span; self.code.push(quote! { if #span.get().remaining() < #wanted { return Err(Error::InvalidLengthError { Loading @@ -277,6 +277,7 @@ impl<'a> FieldParser<'a> { // `size`: the size of the array in number of elements (if // known). If None, the array is a Vec with a dynamic size. size: Option<usize>, padding_size: Option<usize>, decl: Option<&analyzer_ast::Decl>, ) { enum ElementWidth { Loading Loading @@ -312,18 +313,29 @@ impl<'a> FieldParser<'a> { // TODO size modifier // TODO padded_size let span = match padding_size { Some(padding_size) => { let span = self.span; self.check_size(span, "e!(#padding_size)); self.code.push(quote! { let (head, tail) = #span.get().split_at(#padding_size); let mut head = &mut Cell::new(head); #span.replace(tail); }); format_ident!("head") } None => self.span.clone(), }; let id = format_ident!("{id}"); let span = self.span; let parse_element = self.parse_array_element(self.span, width, type_id, decl); let parse_element = self.parse_array_element(&span, width, type_id, decl); match (element_width, &array_shape) { (ElementWidth::Unknown, ArrayShape::SizeField(size_field)) => { // The element width is not known, but the array full // octet size is known by size field. Parse elements // item by item as a vector. self.check_size("e!(#size_field)); self.check_size(&span, "e!(#size_field)); let parse_element = self.parse_array_element(&format_ident!("head"), width, type_id, decl); self.code.push(quote! { Loading Loading @@ -383,7 +395,7 @@ impl<'a> FieldParser<'a> { let element_width = syn::Index::from(element_width); quote!(#count * #element_width) }; self.check_size(&array_size); self.check_size(&span, "e! { #array_size }); self.code.push(quote! { // TODO(mgeisler): use // https://doc.rust-lang.org/std/array/fn.try_from_fn.html Loading @@ -395,10 +407,10 @@ impl<'a> FieldParser<'a> { .map_err(|_| Error::InvalidPacketError)?; }); } (ElementWidth::Static(_), ArrayShape::CountField(count_field)) => { (ElementWidth::Static(element_width), ArrayShape::CountField(count_field)) => { // The element width is known, and the array element // count is known dynamically by the count field. self.check_size("e!(#count_field)); self.check_size(&span, "e!(#count_field * #element_width)); self.code.push(quote! { let #id = (0..#count_field) .map(|_| #parse_element) Loading @@ -411,7 +423,7 @@ impl<'a> FieldParser<'a> { // is known by size field, or unknown (in which case // it is the remaining span length). let array_size = if let ArrayShape::SizeField(size_field) = &array_shape { self.check_size("e!(#size_field)); self.check_size(&span, "e!(#size_field)); quote!(#size_field) } else { quote!(#span.get().remaining()) Loading Loading @@ -459,28 +471,41 @@ impl<'a> FieldParser<'a> { let id = format_ident!("{id}"); let type_id = format_ident!("{type_id}"); match self.scope.get_decl_width(decl, true) { None => self.code.push(quote! { self.code.push(match self.scope.get_decl_width(decl, true) { None => quote! { let #id = #type_id::parse_inner(&mut #span)?; }), }, Some(width) => { assert_eq!(width % 8, 0, "Typedef field type size is not a multiple of 8"); let width = syn::Index::from(width / 8); self.code.push(if let ast::DeclDesc::Checksum { .. } = &decl.desc { // TODO: handle checksum fields. match &decl.desc { ast::DeclDesc::Checksum { .. } => todo!(), ast::DeclDesc::CustomField { .. } if [8, 16, 32, 64].contains(&width) => { let get_uint = types::get_uint(self.endianness, width, span); quote! { #span.get_mut().advance(#width); let #id = #get_uint.into(); } } else { } ast::DeclDesc::CustomField { .. } => { let get_uint = types::get_uint(self.endianness, width, span); quote! { let #id = (#get_uint) .try_into() .unwrap(); // Value is masked and conversion must succeed. } } ast::DeclDesc::Struct { .. } => { let width = syn::Index::from(width / 8); quote! { let (head, tail) = #span.get().split_at(#width); #span.replace(tail); let #id = #type_id::parse(head)?; } }); } _ => unreachable!(), } } }); } /// Parse body and payload fields. fn add_payload_field(&mut self, size_modifier: Option<&str>) { Loading Loading @@ -518,7 +543,7 @@ impl<'a> FieldParser<'a> { // payload and update the span in case fields are placed // after the payload. let size_field = size_field_ident(field_id); self.check_size("e!(#size_field )); self.check_size(self.span, "e!(#size_field )); self.code.push(quote! { let payload = &#span.get()[..#size_field]; #span.get_mut().advance(#size_field); Loading @@ -540,7 +565,7 @@ impl<'a> FieldParser<'a> { "Payload field offset from end of packet is not a multiple of 8" ); let offset_from_end = syn::Index::from(offset_from_end / 8); self.check_size("e!(#offset_from_end)); self.check_size(self.span, "e!(#offset_from_end)); self.code.push(quote! { let payload = &#span.get()[..#span.get().len() - #offset_from_end]; #span.get_mut().advance(payload.len()); Loading
tools/pdl/src/backends/rust/serializer.rs +49 −12 Original line number Diff line number Diff line Loading @@ -55,15 +55,20 @@ impl<'a> FieldSerializer<'a> { pub fn add(&mut self, field: &analyzer_ast::Field) { match &field.desc { _ if self.scope.is_bitfield(field) => self.add_bit_field(field), ast::FieldDesc::Array { id, width, .. } => { self.add_array_field(id, *width, self.scope.get_field_declaration(field)) } ast::FieldDesc::Array { id, width, .. } => self.add_array_field( id, *width, field.annot.padded_size, self.scope.get_field_declaration(field), ), ast::FieldDesc::Typedef { id, type_id } => { self.add_typedef_field(id, type_id); } ast::FieldDesc::Payload { .. } | ast::FieldDesc::Body { .. } => { self.add_payload_field() } // Padding field handled in serialization of associated array field. ast::FieldDesc::Padding { .. } => (), _ => todo!("Cannot yet serialize {field:?}"), } } Loading Loading @@ -258,10 +263,10 @@ impl<'a> FieldSerializer<'a> { &mut self, id: &str, width: Option<usize>, padding_size: Option<usize>, decl: Option<&analyzer_ast::Decl>, ) { // TODO: padding let span = format_ident!("{}", self.span); let serialize = match width { Some(width) => { let value = quote!(*elem); Loading @@ -277,7 +282,6 @@ impl<'a> FieldSerializer<'a> { self.span, ) } else { let span = format_ident!("{}", self.span); quote! { elem.write_to(#span) } Loading @@ -286,10 +290,26 @@ impl<'a> FieldSerializer<'a> { }; let id = format_ident!("{id}"); self.code.push(quote! { self.code.push(match padding_size { Some(padding_size) => quote! { let current_size = #span.len(); for elem in &self.#id { #serialize; } let array_size = #span.len() - current_size; if array_size > #padding_size { panic!("attempted to serialize an array larger than the enclosing padding size"); } #span.put_bytes(0, #padding_size - array_size); }, None => quote! { for elem in &self.#id { #serialize; } } }); } Loading @@ -302,8 +322,25 @@ impl<'a> FieldSerializer<'a> { let id = format_ident!("{id}"); let span = format_ident!("{}", self.span); self.code.push(quote! { self.code.push(match &decl.desc { ast::DeclDesc::Checksum { .. } => todo!(), ast::DeclDesc::CustomField { width: Some(width), .. } => { let backing_type = types::Integer::new(*width); let put_uint = types::put_uint( self.endianness, "e! { #backing_type::from(self.#id) }, *width, self.span, ); quote! { #put_uint; } } ast::DeclDesc::Struct { .. } => quote! { self.#id.write_to(#span); }, _ => unreachable!(), }); } Loading