Skip to content

Commit

Permalink
Add support for _elementsize_ to rust generator
Browse files Browse the repository at this point in the history
  • Loading branch information
DeltaEvo committed May 14, 2024
1 parent 362e2d2 commit 0fa6225
Show file tree
Hide file tree
Showing 17 changed files with 565 additions and 5 deletions.
1 change: 1 addition & 0 deletions pdl-compiler/scripts/generate_cxx_backend_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
8 changes: 8 additions & 0 deletions pdl-compiler/scripts/pdl/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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':
Expand Down
4 changes: 3 additions & 1 deletion pdl-compiler/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
})
}
Expand Down
14 changes: 14 additions & 0 deletions pdl-compiler/src/backends/rust/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
"
Expand Down
55 changes: 51 additions & 4 deletions pdl-compiler/src/backends/rust/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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! {
Expand Down Expand Up @@ -289,6 +295,13 @@ impl<'a> FieldParser<'a> {
}
}

fn find_element_size_field(&self, id: &str) -> Option<proc_macro2::Ident> {
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<usize> {
let decl = self.scope.typedef[self.packet_name];
let mut fields = decl.fields();
Expand Down Expand Up @@ -354,17 +367,20 @@ 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)
} else if let Some(count_field) = self.find_count_field(id) {
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
};
Expand All @@ -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);
Expand Down Expand Up @@ -433,6 +450,35 @@ impl<'a> FieldParser<'a> {
.collect::<Result<Vec<_>, 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.
Expand Down Expand Up @@ -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
Expand Down
44 changes: 44 additions & 0 deletions pdl-compiler/src/backends/rust/serializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions pdl-compiler/src/backends/rust/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@ pub fn generate_tests(input_file: &str) -> Result<String, String> {
"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",
Expand Down
20 changes: 20 additions & 0 deletions pdl-compiler/tests/canonical/le_test_file.pdl
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
38 changes: 38 additions & 0 deletions pdl-compiler/tests/canonical/le_test_vectors.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": [
Expand Down
Loading

0 comments on commit 0fa6225

Please sign in to comment.