Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 10a6ee4d authored by Treehugger Robot's avatar Treehugger Robot Committed by Gerrit Code Review
Browse files

Merge changes If2767284,Ic1289083

* changes:
  pdl: Implement support for array padding
  pdl: Implement custom fields for rust generator
parents b7aaa734 755e1725
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -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",
@@ -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",
+47 −11
Original line number Diff line number Diff line
@@ -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)]
@@ -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 {
@@ -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, .. }
@@ -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 {
@@ -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
@@ -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!(),
        })
@@ -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>(
@@ -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)
}
+106 −0
Original line number Diff line number Diff line
@@ -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,
@@ -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),
    }
}
@@ -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#"
@@ -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,
        "
@@ -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,
        "
+54 −29
Original line number Diff line number Diff line
@@ -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),
@@ -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(&quote!(#wanted));
        self.check_size(self.span, &quote!(#wanted));

        let chunk_type = types::Integer::new(self.shift);
        // TODO(mgeisler): generate Rust variable names which cannot
@@ -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 {
@@ -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 {
@@ -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, &quote!(#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(&quote!(#size_field));
                self.check_size(&span, &quote!(#size_field));
                let parse_element =
                    self.parse_array_element(&format_ident!("head"), width, type_id, decl);
                self.code.push(quote! {
@@ -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, &quote! { #array_size });
                self.code.push(quote! {
                    // TODO(mgeisler): use
                    // https://doc.rust-lang.org/std/array/fn.try_from_fn.html
@@ -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(&quote!(#count_field));
                self.check_size(&span, &quote!(#count_field * #element_width));
                self.code.push(quote! {
                    let #id = (0..#count_field)
                        .map(|_| #parse_element)
@@ -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(&quote!(#size_field));
                    self.check_size(&span, &quote!(#size_field));
                    quote!(#size_field)
                } else {
                    quote!(#span.get().remaining())
@@ -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>) {
@@ -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(&quote!(#size_field ));
            self.check_size(self.span, &quote!(#size_field ));
            self.code.push(quote! {
                let payload = &#span.get()[..#size_field];
                #span.get_mut().advance(#size_field);
@@ -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(&quote!(#offset_from_end));
            self.check_size(self.span, &quote!(#offset_from_end));
            self.code.push(quote! {
                let payload = &#span.get()[..#span.get().len() - #offset_from_end];
                #span.get_mut().advance(payload.len());
+49 −12
Original line number Diff line number Diff line
@@ -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:?}"),
        }
    }
@@ -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);
@@ -277,7 +282,6 @@ impl<'a> FieldSerializer<'a> {
                        self.span,
                    )
                } else {
                    let span = format_ident!("{}", self.span);
                    quote! {
                        elem.write_to(#span)
                    }
@@ -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;
                    }
                }
        });
    }

@@ -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,
                    &quote! { #backing_type::from(self.#id) },
                    *width,
                    self.span,
                );
                quote! {
                    #put_uint;
                }
            }
            ast::DeclDesc::Struct { .. } => quote! {
                self.#id.write_to(#span);
            },
            _ => unreachable!(),
        });
    }

Loading