diff --git a/pdl-compiler/scripts/generate_cxx_backend_tests.py b/pdl-compiler/scripts/generate_cxx_backend_tests.py index 818d376..614d722 100755 --- a/pdl-compiler/scripts/generate_cxx_backend_tests.py +++ b/pdl-compiler/scripts/generate_cxx_backend_tests.py @@ -328,6 +328,7 @@ def run(input: argparse.FileType, output: argparse.FileType, test_vectors: argpa 'Struct_Checksum_Field_FromEnd', 'PartialParent5', 'PartialParent12', + 'Packet_Array_ElementSize_Struct', ] output.write( diff --git a/pdl-compiler/scripts/pdl/ast.py b/pdl-compiler/scripts/pdl/ast.py index 4aff1cc..a8ff968 100644 --- a/pdl-compiler/scripts/pdl/ast.py +++ b/pdl-compiler/scripts/pdl/ast.py @@ -87,6 +87,12 @@ class SizeField(Field): width: int +@node('elementsize_field') +class ElementSize(Field): + field_id: str + width: int + + @node('count_field') class CountField(Field): field_id: str @@ -276,6 +282,8 @@ def convert_(obj: object) -> object: loc = obj['loc'] loc = SourceRange(loc['file'], SourceLocation(**loc['start']), SourceLocation(**loc['end'])) constructor = constructors_.get(kind) + if not constructor: + raise Exception(f'Unknown kind {kind}') members = {'loc': loc, 'kind': kind} for name, value in obj.items(): if name != 'kind' and name != 'loc': diff --git a/pdl-compiler/src/ast.rs b/pdl-compiler/src/ast.rs index e545c5e..ee580c8 100644 --- a/pdl-compiler/src/ast.rs +++ b/pdl-compiler/src/ast.rs @@ -459,7 +459,9 @@ impl Decl { /// if present. pub fn array_size(&self, id: &str) -> Option<&Field> { self.fields().find(|field| match &field.desc { - FieldDesc::Size { field_id, .. } | FieldDesc::Count { field_id, .. } => field_id == id, + FieldDesc::Size { field_id, .. } + | FieldDesc::Count { field_id, .. } + | FieldDesc::ElementSize { field_id, .. } => field_id == id, _ => false, }) } diff --git a/pdl-compiler/src/backends/rust/mod.rs b/pdl-compiler/src/backends/rust/mod.rs index 506de82..e3a3130 100644 --- a/pdl-compiler/src/backends/rust/mod.rs +++ b/pdl-compiler/src/backends/rust/mod.rs @@ -1289,6 +1289,20 @@ mod tests { " ); + test_pdl!( + packet_decl_array_dynamic_element_size, + " + struct Foo { + inner: 8[] + } + packet Bar { + _elementsize_(x): 5, + padding: 3, + x: Foo[] + } + " + ); + test_pdl!( packet_decl_reserved_field, " diff --git a/pdl-compiler/src/backends/rust/parser.rs b/pdl-compiler/src/backends/rust/parser.rs index c50b54d..c1bf0d3 100644 --- a/pdl-compiler/src/backends/rust/parser.rs +++ b/pdl-compiler/src/backends/rust/parser.rs @@ -261,6 +261,12 @@ impl<'a> FieldParser<'a> { let #id = #v as usize; } } + ast::FieldDesc::ElementSize { field_id, .. } => { + let id = format_ident!("{field_id}_element_size"); + quote! { + let #id = #v as usize; + } + } ast::FieldDesc::Count { field_id, .. } => { let id = format_ident!("{field_id}_count"); quote! { @@ -289,6 +295,13 @@ impl<'a> FieldParser<'a> { } } + fn find_element_size_field(&self, id: &str) -> Option { + match self.decl.array_size(id)?.desc { + ast::FieldDesc::ElementSize { .. } => Some(format_ident!("{id}_element_size")), + _ => None, + } + } + fn payload_field_offset_from_end(&self) -> Option { let decl = self.scope.typedef[self.packet_name]; let mut fields = decl.fields(); @@ -354,10 +367,11 @@ impl<'a> FieldParser<'a> { // given via a static count, a count field, a size field, or // unknown. enum ArrayShape { - Static(usize), // Static count - CountField(proc_macro2::Ident), // Count based on count field - SizeField(proc_macro2::Ident), // Count based on size and field - Unknown, // Variable count based on remaining bytes + Static(usize), // Static count + CountField(proc_macro2::Ident), // Count based on count field + SizeField(proc_macro2::Ident), // Count based on size and field + ElementSizeField(proc_macro2::Ident), // Count based on element size and field + Unknown, // Variable count based on remaining bytes } let array_shape = if let Some(count) = size { ArrayShape::Static(count) @@ -365,6 +379,8 @@ impl<'a> FieldParser<'a> { ArrayShape::CountField(count_field) } else if let Some(size_field) = self.find_size_field(id) { ArrayShape::SizeField(size_field) + } else if let Some(element_size_field) = self.find_element_size_field(id) { + ArrayShape::ElementSizeField(element_size_field) } else { ArrayShape::Unknown }; @@ -385,6 +401,7 @@ impl<'a> FieldParser<'a> { None => self.span.clone(), }; + let field_name = id; let id = id.to_ident(); let parse_element = self.parse_array_element(&span, width, type_id, decl); @@ -433,6 +450,35 @@ impl<'a> FieldParser<'a> { .collect::, DecodeError>>()?; }); } + (ElementWidth::Unknown, ArrayShape::ElementSizeField(element_size_field)) => { + // The element width is not known, but the array element + // octet size is known by size field. Parse elements + // item by item as a vector. + let parse_element = + self.parse_array_element(&format_ident!("head"), width, type_id, decl); + let packet_name = self.packet_name; + self.code.push(quote! { + if #span.remaining() % #element_size_field != 0 { + return Err(DecodeError::InvalidLengthError { + obj: #packet_name, + wanted: #element_size_field, + got: #span.remaining() % #element_size_field, + }); + } + let mut #id = Vec::new(); + while !#span.is_empty() { + let (mut head, tail) = #span.split_at(#element_size_field); + #id.push(#parse_element?); + if !head.is_empty() { + return Err(DecodeError::TrailingBytesInArray { + obj: #packet_name, + field: #field_name, + }); + } + #span = tail; + } + }); + } (ElementWidth::Unknown, ArrayShape::Unknown) => { // Neither the count not size is known, parse elements // until the end of the span. @@ -479,6 +525,7 @@ impl<'a> FieldParser<'a> { }); } (ElementWidth::Static(element_width), ArrayShape::SizeField(_)) + | (ElementWidth::Static(element_width), ArrayShape::ElementSizeField(_)) | (ElementWidth::Static(element_width), ArrayShape::Unknown) => { // The element width is known, and the array full size // is known by size field, or unknown (in which case diff --git a/pdl-compiler/src/backends/rust/serializer.rs b/pdl-compiler/src/backends/rust/serializer.rs index a4a8890..4d18883 100644 --- a/pdl-compiler/src/backends/rust/serializer.rs +++ b/pdl-compiler/src/backends/rust/serializer.rs @@ -377,6 +377,50 @@ impl Encoder { shift, }); } + ast::FieldDesc::ElementSize { field_id, width, .. } => { + let field_name = field_id.to_ident(); + let field_type = types::Integer::new(*width); + let field_element_size_name = format_ident!("{field_id}_element_size"); + let packet_name = &self.packet_name; + self.tokens.extend(quote! { + let #field_element_size_name = self.#field_name + .get(0) + .map_or(0, Packet::encoded_len); + + for (element_index, element) in self.#field_name.iter().enumerate() { + if element.encoded_len() != #field_element_size_name { + return Err(EncodeError::InvalidArrayElementSize { + packet: #packet_name, + field: #field_id, + size: element.encoded_len(), + expected_size: #field_element_size_name, + element_index, + }) + } + } + }); + if field_type.width > *width { + let max_value = mask_bits(*width, "usize"); + self.tokens.extend(quote! { + if #field_element_size_name > #max_value { + return Err(EncodeError::SizeOverflow { + packet: #packet_name, + field: #field_id, + size: #field_element_size_name, + maximum_size: #max_value, + }) + } + }); + } + self.tokens.extend(quote! { + let #field_element_size_name = #field_element_size_name as #field_type; + }); + self.bit_fields.push(BitField { + value: quote!(#field_element_size_name), + field_type, + shift, + }); + } ast::FieldDesc::Count { field_id, width, .. } => { let field_name = field_id.to_ident(); let field_type = types::Integer::new(*width); diff --git a/pdl-compiler/src/backends/rust/test.rs b/pdl-compiler/src/backends/rust/test.rs index 679b289..6ace1ed 100644 --- a/pdl-compiler/src/backends/rust/test.rs +++ b/pdl-compiler/src/backends/rust/test.rs @@ -172,6 +172,7 @@ pub fn generate_tests(input_file: &str) -> Result { "Packet_Array_Field_UnsizedElement_VariableSize", "Packet_Array_Field_SizedElement_VariableSize_Padded", "Packet_Array_Field_UnsizedElement_VariableCount_Padded", + "Packet_Array_ElementSize_Struct", "Packet_Optional_Scalar_Field", "Packet_Optional_Enum_Field", "Packet_Optional_Struct_Field", diff --git a/pdl-compiler/tests/canonical/le_test_file.pdl b/pdl-compiler/tests/canonical/le_test_file.pdl index 833b723..fb6a35f 100644 --- a/pdl-compiler/tests/canonical/le_test_file.pdl +++ b/pdl-compiler/tests/canonical/le_test_file.pdl @@ -362,6 +362,26 @@ packet Packet_Array_Field_UnsizedElement_VariableCount_Padded { _padding_ [16], } +struct Packet_Array_ElementSize_Struct_ { + array: 8[], +} + +packet Packet_Array_ElementSize_Struct { + _elementsize_(array) : 2, + _reserved_: 6, + array: Packet_Array_ElementSize_Struct_[], +} + +packet Packet_Array_ElementSize_UnsizedCustomField { + _elementsize_(array) : 8, + array: UnsizedCustomField[], +} + +packet Packet_Array_ElementSize_SizedCustomField { + _elementsize_(array) : 8, + array: SizedCustomField[], +} + packet Packet_Optional_Scalar_Field { c0: 1, c1: 1, diff --git a/pdl-compiler/tests/canonical/le_test_vectors.json b/pdl-compiler/tests/canonical/le_test_vectors.json index e6fb3b8..585f571 100644 --- a/pdl-compiler/tests/canonical/le_test_vectors.json +++ b/pdl-compiler/tests/canonical/le_test_vectors.json @@ -2154,6 +2154,44 @@ } ] }, + { + "packet": "Packet_Array_ElementSize_Struct", + "tests": [ + { + "packed": "012a", + "unpacked": { + "array": [ + { + "array": [42] + } + ] + } + }, + { + "packed": "012a2b", + "unpacked": { + "array": [ + { + "array": [42] + }, + { + "array": [43] + } + ] + } + }, + { + "packed": "022a2b", + "unpacked": { + "array": [ + { + "array": [42, 43] + } + ] + } + } + ] + }, { "packet": "Packet_Optional_Scalar_Field", "tests": [ diff --git a/pdl-compiler/tests/generated/rust/packet_decl_array_dynamic_element_size_big_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_array_dynamic_element_size_big_endian.rs new file mode 100644 index 0000000..7a3caf2 --- /dev/null +++ b/pdl-compiler/tests/generated/rust/packet_decl_array_dynamic_element_size_big_endian.rs @@ -0,0 +1,165 @@ +#![rustfmt::skip] +/// @generated rust packets from test. +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use std::convert::{TryFrom, TryInto}; +use std::cell::Cell; +use std::fmt; +use std::result::Result; +use pdl_runtime::{DecodeError, EncodeError, Packet}; +/// Private prevents users from creating arbitrary scalar values +/// in situations where the value needs to be validated. +/// Users can freely deref the value, but only the backend +/// may create it. +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Private(T); +impl std::ops::Deref for Private { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl std::fmt::Debug for Private { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::fmt(&self.0, f) + } +} +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Foo { + pub inner: Vec, +} +impl TryFrom<&Foo> for Bytes { + type Error = EncodeError; + fn try_from(packet: &Foo) -> Result { + packet.encode_to_bytes() + } +} +impl TryFrom<&Foo> for Vec { + type Error = EncodeError; + fn try_from(packet: &Foo) -> Result { + packet.encode_to_vec() + } +} +impl Foo { + pub fn inner(&self) -> &Vec { + &self.inner + } +} +impl Packet for Foo { + fn encoded_len(&self) -> usize { + self.inner.len() * 1 + } + fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { + for elem in &self.inner { + buf.put_u8(*elem); + } + Ok(()) + } + fn decode(mut buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + let mut inner = Vec::with_capacity(buf.remaining()); + for _ in 0..buf.remaining() { + inner.push(Ok::<_, DecodeError>(buf.get_u8())?); + } + Ok((Self { inner }, buf)) + } +} +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Bar { + pub padding: u8, + pub x: Vec, +} +impl TryFrom<&Bar> for Bytes { + type Error = EncodeError; + fn try_from(packet: &Bar) -> Result { + packet.encode_to_bytes() + } +} +impl TryFrom<&Bar> for Vec { + type Error = EncodeError; + fn try_from(packet: &Bar) -> Result { + packet.encode_to_vec() + } +} +impl Bar { + pub fn padding(&self) -> u8 { + self.padding + } + pub fn x(&self) -> &Vec { + &self.x + } +} +impl Packet for Bar { + fn encoded_len(&self) -> usize { + 1 + self.x.iter().map(Packet::encoded_len).sum::() + } + fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { + let x_element_size = self.x.get(0).map_or(0, Packet::encoded_len); + for (element_index, element) in self.x.iter().enumerate() { + if element.encoded_len() != x_element_size { + return Err(EncodeError::InvalidArrayElementSize { + packet: "Bar", + field: "x", + size: element.encoded_len(), + expected_size: x_element_size, + element_index, + }); + } + } + if x_element_size > 0x1f { + return Err(EncodeError::SizeOverflow { + packet: "Bar", + field: "x", + size: x_element_size, + maximum_size: 0x1f, + }); + } + let x_element_size = x_element_size as u8; + if self.padding() > 0x7 { + return Err(EncodeError::InvalidScalarValue { + packet: "Bar", + field: "padding", + value: self.padding() as u64, + maximum_value: 0x7 as u64, + }); + } + let value = x_element_size | (self.padding() << 5); + buf.put_u8(value); + for elem in &self.x { + elem.encode(buf)?; + } + Ok(()) + } + fn decode(mut buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + if buf.remaining() < 1 { + return Err(DecodeError::InvalidLengthError { + obj: "Bar", + wanted: 1, + got: buf.remaining(), + }); + } + let chunk = buf.get_u8(); + let x_element_size = (chunk & 0x1f) as usize; + let padding = ((chunk >> 5) & 0x7); + if buf.remaining() % x_element_size != 0 { + return Err(DecodeError::InvalidLengthError { + obj: "Bar", + wanted: x_element_size, + got: buf.remaining() % x_element_size, + }); + } + let mut x = Vec::new(); + while !buf.is_empty() { + let (mut head, tail) = buf.split_at(x_element_size); + x.push(Foo::decode_mut(&mut head)?); + if !head.is_empty() { + return Err(DecodeError::TrailingBytesInArray { + obj: "Bar", + field: "x", + }); + } + buf = tail; + } + Ok((Self { padding, x }, buf)) + } +} diff --git a/pdl-compiler/tests/generated/rust/packet_decl_array_dynamic_element_size_little_endian.rs b/pdl-compiler/tests/generated/rust/packet_decl_array_dynamic_element_size_little_endian.rs new file mode 100644 index 0000000..7a3caf2 --- /dev/null +++ b/pdl-compiler/tests/generated/rust/packet_decl_array_dynamic_element_size_little_endian.rs @@ -0,0 +1,165 @@ +#![rustfmt::skip] +/// @generated rust packets from test. +use bytes::{Buf, BufMut, Bytes, BytesMut}; +use std::convert::{TryFrom, TryInto}; +use std::cell::Cell; +use std::fmt; +use std::result::Result; +use pdl_runtime::{DecodeError, EncodeError, Packet}; +/// Private prevents users from creating arbitrary scalar values +/// in situations where the value needs to be validated. +/// Users can freely deref the value, but only the backend +/// may create it. +#[derive(Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct Private(T); +impl std::ops::Deref for Private { + type Target = T; + fn deref(&self) -> &Self::Target { + &self.0 + } +} +impl std::fmt::Debug for Private { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + T::fmt(&self.0, f) + } +} +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Foo { + pub inner: Vec, +} +impl TryFrom<&Foo> for Bytes { + type Error = EncodeError; + fn try_from(packet: &Foo) -> Result { + packet.encode_to_bytes() + } +} +impl TryFrom<&Foo> for Vec { + type Error = EncodeError; + fn try_from(packet: &Foo) -> Result { + packet.encode_to_vec() + } +} +impl Foo { + pub fn inner(&self) -> &Vec { + &self.inner + } +} +impl Packet for Foo { + fn encoded_len(&self) -> usize { + self.inner.len() * 1 + } + fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { + for elem in &self.inner { + buf.put_u8(*elem); + } + Ok(()) + } + fn decode(mut buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + let mut inner = Vec::with_capacity(buf.remaining()); + for _ in 0..buf.remaining() { + inner.push(Ok::<_, DecodeError>(buf.get_u8())?); + } + Ok((Self { inner }, buf)) + } +} +#[derive(Debug, Clone, PartialEq, Eq)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +pub struct Bar { + pub padding: u8, + pub x: Vec, +} +impl TryFrom<&Bar> for Bytes { + type Error = EncodeError; + fn try_from(packet: &Bar) -> Result { + packet.encode_to_bytes() + } +} +impl TryFrom<&Bar> for Vec { + type Error = EncodeError; + fn try_from(packet: &Bar) -> Result { + packet.encode_to_vec() + } +} +impl Bar { + pub fn padding(&self) -> u8 { + self.padding + } + pub fn x(&self) -> &Vec { + &self.x + } +} +impl Packet for Bar { + fn encoded_len(&self) -> usize { + 1 + self.x.iter().map(Packet::encoded_len).sum::() + } + fn encode(&self, buf: &mut impl BufMut) -> Result<(), EncodeError> { + let x_element_size = self.x.get(0).map_or(0, Packet::encoded_len); + for (element_index, element) in self.x.iter().enumerate() { + if element.encoded_len() != x_element_size { + return Err(EncodeError::InvalidArrayElementSize { + packet: "Bar", + field: "x", + size: element.encoded_len(), + expected_size: x_element_size, + element_index, + }); + } + } + if x_element_size > 0x1f { + return Err(EncodeError::SizeOverflow { + packet: "Bar", + field: "x", + size: x_element_size, + maximum_size: 0x1f, + }); + } + let x_element_size = x_element_size as u8; + if self.padding() > 0x7 { + return Err(EncodeError::InvalidScalarValue { + packet: "Bar", + field: "padding", + value: self.padding() as u64, + maximum_value: 0x7 as u64, + }); + } + let value = x_element_size | (self.padding() << 5); + buf.put_u8(value); + for elem in &self.x { + elem.encode(buf)?; + } + Ok(()) + } + fn decode(mut buf: &[u8]) -> Result<(Self, &[u8]), DecodeError> { + if buf.remaining() < 1 { + return Err(DecodeError::InvalidLengthError { + obj: "Bar", + wanted: 1, + got: buf.remaining(), + }); + } + let chunk = buf.get_u8(); + let x_element_size = (chunk & 0x1f) as usize; + let padding = ((chunk >> 5) & 0x7); + if buf.remaining() % x_element_size != 0 { + return Err(DecodeError::InvalidLengthError { + obj: "Bar", + wanted: x_element_size, + got: buf.remaining() % x_element_size, + }); + } + let mut x = Vec::new(); + while !buf.is_empty() { + let (mut head, tail) = buf.split_at(x_element_size); + x.push(Foo::decode_mut(&mut head)?); + if !head.is_empty() { + return Err(DecodeError::TrailingBytesInArray { + obj: "Bar", + field: "x", + }); + } + buf = tail; + } + Ok((Self { padding, x }, buf)) + } +} diff --git a/pdl-compiler/tests/python_generator_test.py b/pdl-compiler/tests/python_generator_test.py index 21ab14b..6e80966 100644 --- a/pdl-compiler/tests/python_generator_test.py +++ b/pdl-compiler/tests/python_generator_test.py @@ -32,6 +32,11 @@ import be_backend +SKIPPED_TESTS = [ + "Packet_Array_ElementSize_Struct" +] + + def match_object(self, left, right): """Recursively match a python class object against a reference json object.""" @@ -90,6 +95,10 @@ def testLittleEndian(self): # selected packet. packet = item['packet'] tests = item['tests'] + + if packet in SKIPPED_TESTS: + continue + with self.subTest(packet=packet): # Retrieve the class object from the generated # module, in order to invoke the proper parse @@ -109,6 +118,10 @@ def testBigEndian(self): # selected packet. packet = item['packet'] tests = item['tests'] + + if packet in SKIPPED_TESTS: + continue + with self.subTest(packet=packet): # Retrieve the class object from the generated # module, in order to invoke the proper constructor @@ -133,6 +146,10 @@ def testLittleEndian(self): # selected packet. packet = item['packet'] tests = item['tests'] + + if packet in SKIPPED_TESTS: + continue + with self.subTest(packet=packet): # Retrieve the class object from the generated # module, in order to invoke the proper constructor @@ -153,6 +170,10 @@ def testBigEndian(self): # selected packet. packet = item['packet'] tests = item['tests'] + + if packet in SKIPPED_TESTS: + continue + with self.subTest(packet=packet): # Retrieve the class object from the generated # module, in order to invoke the proper parse diff --git a/pdl-compiler/tests/run_cxx_generator_tests.sh b/pdl-compiler/tests/run_cxx_generator_tests.sh index b54b311..9063ecf 100755 --- a/pdl-compiler/tests/run_cxx_generator_tests.sh +++ b/pdl-compiler/tests/run_cxx_generator_tests.sh @@ -22,6 +22,9 @@ python3 scripts/generate_cxx_backend.py \ --exclude-declaration Packet_Custom_Field_VariableSize \ --exclude-declaration Packet_Checksum_Field_FromStart \ --exclude-declaration Packet_Checksum_Field_FromEnd \ + --exclude-declaration Packet_Array_ElementSize_Struct \ + --exclude-declaration Packet_Array_ElementSize_UnsizedCustomField \ + --exclude-declaration Packet_Array_ElementSize_SizedCustomField \ --exclude-declaration Struct_Custom_Field_ConstantSize \ --exclude-declaration Struct_Custom_Field_VariableSize \ --exclude-declaration Struct_Checksum_Field_FromStart \ @@ -44,6 +47,9 @@ python3 scripts/generate_cxx_backend.py \ --exclude-declaration Packet_Custom_Field_VariableSize \ --exclude-declaration Packet_Checksum_Field_FromStart \ --exclude-declaration Packet_Checksum_Field_FromEnd \ + --exclude-declaration Packet_Array_ElementSize_Struct \ + --exclude-declaration Packet_Array_ElementSize_UnsizedCustomField \ + --exclude-declaration Packet_Array_ElementSize_SizedCustomField \ --exclude-declaration Struct_Custom_Field_ConstantSize \ --exclude-declaration Struct_Custom_Field_VariableSize \ --exclude-declaration Struct_Checksum_Field_FromStart \ diff --git a/pdl-compiler/tests/run_python_generator_tests.sh b/pdl-compiler/tests/run_python_generator_tests.sh index e11b8f5..19f742a 100755 --- a/pdl-compiler/tests/run_python_generator_tests.sh +++ b/pdl-compiler/tests/run_python_generator_tests.sh @@ -18,10 +18,16 @@ pdlc "$OUT_DIR"/be_test_file.pdl > "$OUT_DIR"/be_test_file.json python3 scripts/generate_python_backend.py \ --input "$OUT_DIR"/le_test_file.json \ --output "$OUT_DIR"/le_backend.py \ + --exclude-declaration Packet_Array_ElementSize_Struct \ + --exclude-declaration Packet_Array_ElementSize_UnsizedCustomField \ + --exclude-declaration Packet_Array_ElementSize_SizedCustomField \ --custom-type-location tests.custom_types python3 scripts/generate_python_backend.py \ --input "$OUT_DIR"/be_test_file.json \ --output "$OUT_DIR"/be_backend.py \ + --exclude-declaration Packet_Array_ElementSize_Struct \ + --exclude-declaration Packet_Array_ElementSize_UnsizedCustomField \ + --exclude-declaration Packet_Array_ElementSize_SizedCustomField \ --custom-type-location tests.custom_types export PYTHONPATH="$OUT_DIR:.:${PYTHONPATH:-}" diff --git a/pdl-compiler/tests/run_rust_generator_tests.sh b/pdl-compiler/tests/run_rust_generator_tests.sh index 290f223..8d0eb67 100755 --- a/pdl-compiler/tests/run_rust_generator_tests.sh +++ b/pdl-compiler/tests/run_rust_generator_tests.sh @@ -37,6 +37,8 @@ cargo run --bin pdlc -- \ --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier \ --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_ \ --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier \ + --exclude-declaration Packet_Array_ElementSize_UnsizedCustomField \ + --exclude-declaration Packet_Array_ElementSize_SizedCustomField \ > "$OUT_DIR/canonical_test/src/le_backend.rs" cargo run --bin pdlc -- \ tests/canonical/le_test_vectors.json \ @@ -60,6 +62,8 @@ cargo run --bin pdlc -- \ --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier \ --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_ \ --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier \ + --exclude-declaration Packet_Array_ElementSize_UnsizedCustomField \ + --exclude-declaration Packet_Array_ElementSize_SizedCustomField \ > "$OUT_DIR/canonical_test/src/be_backend.rs" cargo run --bin pdlc -- \ tests/canonical/be_test_vectors.json \ diff --git a/pdl-compiler/tests/run_rust_legacy_generator_tests.sh b/pdl-compiler/tests/run_rust_legacy_generator_tests.sh index afb7149..509bea3 100755 --- a/pdl-compiler/tests/run_rust_legacy_generator_tests.sh +++ b/pdl-compiler/tests/run_rust_legacy_generator_tests.sh @@ -37,6 +37,9 @@ cargo run --bin pdlc -- \ --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier \ --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_ \ --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier \ + --exclude-declaration Packet_Array_ElementSize_Struct \ + --exclude-declaration Packet_Array_ElementSize_UnsizedCustomField \ + --exclude-declaration Packet_Array_ElementSize_SizedCustomField \ > "$OUT_DIR/canonical_test/src/le_backend.rs" cargo run --bin pdlc -- \ tests/canonical/le_test_vectors.json \ @@ -60,6 +63,9 @@ cargo run --bin pdlc -- \ --exclude-declaration Packet_Array_Field_UnsizedElement_SizeModifier \ --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier_ \ --exclude-declaration Struct_Array_Field_UnsizedElement_SizeModifier \ + --exclude-declaration Packet_Array_ElementSize_Struct \ + --exclude-declaration Packet_Array_ElementSize_UnsizedCustomField \ + --exclude-declaration Packet_Array_ElementSize_SizedCustomField \ > "$OUT_DIR/canonical_test/src/be_backend.rs" cargo run --bin pdlc -- \ tests/canonical/be_test_vectors.json \ diff --git a/pdl-runtime/src/lib.rs b/pdl-runtime/src/lib.rs index a5c7a78..e84adce 100644 --- a/pdl-runtime/src/lib.rs +++ b/pdl-runtime/src/lib.rs @@ -42,6 +42,8 @@ pub enum DecodeError { InvalidChildError { expected: &'static str, actual: String }, #[error("packet has trailing bytes")] TrailingBytes, + #[error("packet has trailing bytes inside {obj}.{field} array")] + TrailingBytesInArray { obj: &'static str, field: &'static str }, } /// Type of serialization errors. @@ -57,6 +59,16 @@ pub enum EncodeError { "the value of {packet}::{field} ({value}) is outside the range of valid values 0..{maximum_value}" )] InvalidScalarValue { packet: &'static str, field: &'static str, value: u64, maximum_value: u64 }, + #[error( + "{packet}.{field}[{element_index}] size is {size}, but {expected_size} was expected (size of {packet}.{field}[0])" + )] + InvalidArrayElementSize { + packet: &'static str, + field: &'static str, + size: usize, + expected_size: usize, + element_index: usize, + }, } /// Trait implemented for all toplevel packet declarations.