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

Commit 6c1556ee authored by Martin Geisler's avatar Martin Geisler
Browse files

pdl: apply zero-padding correctly on the MSB side of the payload

Before we would always do the zero-padding after the payload. This is
wrong for a big-endian encoding where we need to apply the padding to
the left of the payload instead.

As an example, consider a 24 bit field which is decoded into a 32 bit
Rust value. In little-endian encoding, 3 bytes with values

    0xC0 0xB0 0xA0

should be decoded to the integer 0xA0B0C0. Applying the padding after
the bytes is correct here:

    0xC0 0xB0 0xA0 0x00

is the little-endian encoding of 0xA0B0C0. For big-endian, the correct
encoding is instead

    0x00 0xA0 0xB0 0xC0

Bug: 239038416
Test: atest pdl_inline_tests
Change-Id: I7b28dd99394fdfe4a78a47c453bd7a1e7a74cecb
parent 0e385aee
Loading
Loading
Loading
Loading
+99 −3
Original line number Original line Diff line number Diff line
@@ -152,9 +152,17 @@ fn generate_field_parser(
                ast::EndiannessValue::LittleEndian => format_ident!("from_le_bytes"),
                ast::EndiannessValue::LittleEndian => format_ident!("from_le_bytes"),
            };
            };


            // We need the padding on the MSB side of the payload, so
            // for big-endian, we need to padding on the left, for
            // little-endian we need it on the right.
            let padding = vec![syn::Index::from(0); (type_width - width) / 8];
            let (padding_before, padding_after) = match endianness_value {
                ast::EndiannessValue::BigEndian => (padding, vec![]),
                ast::EndiannessValue::LittleEndian => (vec![], padding),
            };

            let wanted_len = syn::Index::from(offset + width / 8);
            let wanted_len = syn::Index::from(offset + width / 8);
            let indices = (offset..offset + width / 8).map(syn::Index::from);
            let indices = (offset..offset + width / 8).map(syn::Index::from);
            let padding = vec![syn::Index::from(0); (type_width - width) / 8];
            let mask = if *width != type_width {
            let mask = if *width != type_width {
                Some(quote! {
                Some(quote! {
                    let #field_name = #field_name & 0xfff;
                    let #field_name = #field_name & 0xfff;
@@ -174,7 +182,9 @@ fn generate_field_parser(
                        got: bytes.len(),
                        got: bytes.len(),
                    });
                    });
                }
                }
                let #field_name = #field_type::#getter([#(bytes[#indices]),* #(, #padding)*]);
                let #field_name = #field_type::#getter([
                    #(#padding_before,)* #(bytes[#indices]),* #(, #padding_after)*
                ]);
                #mask
                #mask
            }
            }
        }
        }
@@ -528,7 +538,7 @@ mod tests {
    use super::*;
    use super::*;
    use crate::ast;
    use crate::ast;
    use crate::parser::parse_inline;
    use crate::parser::parse_inline;
    use crate::test_utils::{assert_snapshot_eq, rustfmt};
    use crate::test_utils::{assert_eq_with_diff, assert_snapshot_eq, rustfmt};


    /// Parse a string fragment as a PDL file.
    /// Parse a string fragment as a PDL file.
    ///
    ///
@@ -604,4 +614,90 @@ mod tests {
            &rustfmt(&actual_code),
            &rustfmt(&actual_code),
        );
        );
    }
    }

    // Assert that an expression equals the given expression.
    //
    // Both expressions are wrapped in a `main` function (so we can
    // format it with `rustfmt`) and a diff is be shown if they
    // differ.
    #[track_caller]
    fn assert_expr_eq(left: proc_macro2::TokenStream, right: proc_macro2::TokenStream) {
        let left = quote! {
            fn main() { #left }
        };
        let right = quote! {
            fn main() { #right }
        };
        assert_eq_with_diff(
            "left",
            &rustfmt(&left.to_string()),
            "right",
            &rustfmt(&right.to_string()),
        );
    }

    #[test]
    fn test_generate_field_parser_no_padding() {
        let loc = ast::SourceRange::default();
        let field = ast::Field::Scalar { loc, id: String::from("a"), width: 8 };

        assert_expr_eq(
            generate_field_parser(&ast::EndiannessValue::BigEndian, "Foo", &field, 10),
            quote! {
                if bytes.len() < 11 {
                    return Err(Error::InvalidLengthError {
                        obj: "Foo".to_string(),
                        field: "a".to_string(),
                        wanted: 11,
                        got: bytes.len(),
                    });
                }
                let a = u8::from_be_bytes([bytes[10]]);
            },
        );
    }

    #[test]
    fn test_generate_field_parser_little_endian_padding() {
        // Test with width != type width.
        let loc = ast::SourceRange::default();
        let field = ast::Field::Scalar { loc, id: String::from("a"), width: 24 };
        assert_expr_eq(
            generate_field_parser(&ast::EndiannessValue::LittleEndian, "Foo", &field, 10),
            quote! {
                if bytes.len() < 13 {
                    return Err(Error::InvalidLengthError {
                        obj: "Foo".to_string(),
                        field: "a".to_string(),
                        wanted: 13,
                        got: bytes.len(),
                    });
                }
                let a = u32::from_le_bytes([bytes[10], bytes[11], bytes[12], 0]);
                let a = a & 0xfff;
            },
        );
    }

    #[test]
    fn test_generate_field_parser_big_endian_padding() {
        // Test with width != type width.
        let loc = ast::SourceRange::default();
        let field = ast::Field::Scalar { loc, id: String::from("a"), width: 24 };
        assert_expr_eq(
            generate_field_parser(&ast::EndiannessValue::BigEndian, "Foo", &field, 10),
            quote! {
                if bytes.len() < 13 {
                    return Err(Error::InvalidLengthError {
                        obj: "Foo".to_string(),
                        field: "a".to_string(),
                        wanted: 13,
                        got: bytes.len(),
                    });
                }
                let a = u32::from_be_bytes([0, bytes[10], bytes[11], bytes[12]]);
                let a = a & 0xfff;
            },
        );
    }
}
}