From c9ee529341e8f8e553bcb6299d8fe38dbe9fa6d1 Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Fri, 13 Oct 2023 00:41:26 +0200 Subject: [PATCH 1/7] Added prepend attribute Still working on adding it to the pack call --- packed_struct_codegen/src/pack.rs | 3 +- packed_struct_codegen/src/pack_codegen.rs | 28 +++++ packed_struct_codegen/src/pack_parse.rs | 106 +++++++++++++++--- .../src/pack_parse_attributes.rs | 27 +++-- 4 files changed, 143 insertions(+), 21 deletions(-) diff --git a/packed_struct_codegen/src/pack.rs b/packed_struct_codegen/src/pack.rs index 2129b56..3612bf5 100644 --- a/packed_struct_codegen/src/pack.rs +++ b/packed_struct_codegen/src/pack.rs @@ -49,7 +49,8 @@ pub struct PackStruct<'a> { pub num_bytes: usize, pub num_bits: usize, pub data_struct: &'a syn::DataStruct, - pub derive_input: &'a syn::DeriveInput + pub derive_input: &'a syn::DeriveInput, + pub prepend: Option, } diff --git a/packed_struct_codegen/src/pack_codegen.rs b/packed_struct_codegen/src/pack_codegen.rs index 2762e67..0380d63 100644 --- a/packed_struct_codegen/src/pack_codegen.rs +++ b/packed_struct_codegen/src/pack_codegen.rs @@ -4,6 +4,7 @@ extern crate syn; use crate::pack::*; use crate::pack_codegen_docs::*; use crate::common::*; +use crate::pack_parse::Prepend; use syn::spanned::Spanned; use crate::utils::*; @@ -17,6 +18,8 @@ pub fn derive_pack(parsed: &PackStruct) -> syn::Result let type_documentation = type_docs(parsed); let num_bytes = parsed.num_bytes; let num_bits = parsed.num_bits; + + let prepend = &parsed.prepend; let mut pack_fields = vec![]; @@ -79,6 +82,31 @@ pub fn derive_pack(parsed: &PackStruct) -> syn::Result } } + //TODO: Move prepend check to before the reg. Either make target be a slice into target after the header or pass an offset into pack_bits to make sure headers don't get overwritten + // Call teh trait method if the enum is set to that. Add the trait. + // Do the same stuff for append + // The trait should return a result, in case we want to verify that the header is correct before unpacking fore example. Or if we want to check the xor appended at the end + // In case we just have the bytes array, we do need to remove the header in unpack! + + if let Some(pre) = prepend { + match pre { + Prepend::Trait => { + // pack_fields.push(quote! { + // { + // let packed = { #pack }; + // #pack_bits + // } + // }); + }, + Prepend::Bytes(bytes) => { + pack_fields.push(quote! { + assert!(#num_bytes >= bytes.len(), "Packed bytes array is smaller than prepend array even though we added the prepend length!"); + std::ptr::copy_nonoverlapping(target, bytes, bytes.len()); + }); + }, + } + } + } let result_ty = result_type(); diff --git a/packed_struct_codegen/src/pack_parse.rs b/packed_struct_codegen/src/pack_parse.rs index ff27bfe..d1fb99a 100644 --- a/packed_struct_codegen/src/pack_parse.rs +++ b/packed_struct_codegen/src/pack_parse.rs @@ -4,6 +4,7 @@ extern crate syn; use crate::pack::*; use crate::pack_parse_attributes::*; +use proc_macro2::Span; use syn::Meta; use syn::Token; use syn::punctuated::Punctuated; @@ -14,8 +15,8 @@ use std::ops::Range; use crate::utils_syn::{get_expr_int_val, get_single_segment, tokens_to_string}; -pub fn parse_sub_attributes(attributes: &[syn::Attribute], main_attribute: &str, wrong_attribute: &str) -> syn::Result> { - let mut r = vec![]; +pub fn parse_sub_attributes(attributes: &[syn::Attribute], main_attribute: &str, wrong_attribute: &str) -> syn::Result> { + let mut r: Vec<(String, syn::Expr)> = vec![]; for attr in attributes { if attr.path().is_ident(wrong_attribute) { @@ -31,13 +32,16 @@ pub fn parse_sub_attributes(attributes: &[syn::Attribute], main_attribute: &str, }; for meta in nested { match meta { - syn::Meta::Path(_) => (), + syn::Meta::Path(path) => { + if let Some(key) = path.get_ident() { + // Not the nicest of hacks, but I did not want to make the syn::Expr in the vec an option as well + r.push((key.to_string(), syn::Expr::Lit(syn::ExprLit{ attrs: vec![],lit: syn::Lit::Bool(syn::LitBool { value: true, span: Span::call_site()})}))); + } + }, syn::Meta::List(_) => (), syn::Meta::NameValue(nv) => { - if let (Some(key), syn::Expr::Lit(lit)) = (nv.path.get_ident(), &nv.value) { - if let syn::Lit::Str(lit) = &lit.lit { - r.push((key.to_string(), lit.value())); - } + if let (Some(key), lit) = (nv.path.get_ident(), &nv.value) { + r.push((key.to_string(), lit.clone())); } } } @@ -48,6 +52,26 @@ pub fn parse_sub_attributes(attributes: &[syn::Attribute], main_attribute: &str, Ok(r) } +pub fn parse_sub_attributes_as_string(attributes: &[syn::Attribute], main_attribute: &str, wrong_attribute: &str) -> syn::Result> { + parse_sub_attributes(attributes, main_attribute, wrong_attribute).and_then(|vec| { + Ok(vec.into_iter().map(|(key, expr)| { + (key, get_string_from_expr(&expr).unwrap_or_default()) + }).collect()) + }) +} + +pub fn get_string_from_expr(expr: &syn::Expr) -> Option { + if let syn::Expr::Lit(lit) = expr { + if let syn::Lit::Str(lit) = &lit.lit { + Some(lit.value()) + } else { + None + } + } else { + None + } +} + #[derive(Clone, Copy, Debug, PartialEq, Eq)] /// https://en.wikipedia.org/wiki/Bit_numbering @@ -86,6 +110,52 @@ impl IntegerEndianness { } } +#[derive(Clone, Debug)] +/// https://en.wikipedia.org/wiki/Bit_numbering +pub enum Prepend{ + Bytes(Vec), + Trait, +} + +fn get_bytes_from_expr_lit(bytes: &mut Vec, use_trait: &mut bool, expr_lit: &syn::ExprLit) { + match &expr_lit.lit { + syn::Lit::Byte( byte) => bytes.push(byte.value()), + syn::Lit::ByteStr(st) => bytes.append(&mut st.value()), + syn::Lit::Int(i) => { + if let Ok(val) = i.base10_parse::() { + bytes.push(val); + } + }, + syn::Lit::Bool(bl) => *use_trait = bl.value(), + _ => () + } +} + +fn get_bytes_from_expr(bytes: &mut Vec, use_trait: &mut bool, expr: &syn::Expr) { + match expr { + syn::Expr::Lit(lit) => get_bytes_from_expr_lit(bytes, use_trait, lit), + syn::Expr::Array(arr) => { + arr.elems.iter().for_each(|expr| get_bytes_from_expr(bytes, use_trait, &expr) ) + }, + _ => (), + } +} + +impl Prepend { + pub fn from_expr(expr: &syn::Expr) -> Option { + let mut bytes = vec![]; + let mut use_trait = false; + get_bytes_from_expr(&mut bytes, &mut use_trait, expr); + if use_trait { + Some(Prepend::Trait) + } else if bytes.len() > 0 { + Some(Prepend::Bytes(bytes)) + } else { + None + } + } +} + fn get_builtin_type_bit_width(p: &syn::PathSegment) -> syn::Result> { match p.ident.to_string().as_str() { @@ -166,7 +236,7 @@ fn get_field_mid_positioning(field: &syn::Field) -> syn::Result { return Err(syn::Error::new(field.ty.span(), "Unsupported type")); } }; - let field_attributes = PackFieldAttribute::parse_all(&parse_sub_attributes(&field.attrs, "packed_field", "packed_struct")?); + let field_attributes = PackFieldAttribute::parse_all(&parse_sub_attributes_as_string(&field.attrs, "packed_field", "packed_struct")?); let bits_position = field_attributes.iter().filter_map(|a| match a { &PackFieldAttribute::BitPosition(b) | &PackFieldAttribute::BytePosition(b) => Some(b), @@ -242,7 +312,7 @@ fn parse_reg_field(field: &syn::Field, ty: &syn::Type, bit_range: &Range, let bit_width = (bit_range.end - bit_range.start) + 1; let ty_str = tokens_to_string(ty); - let field_attributes = PackFieldAttribute::parse_all(&parse_sub_attributes(&field.attrs, "packed_field", "packed_struct")?); + let field_attributes = PackFieldAttribute::parse_all(&parse_sub_attributes_as_string(&field.attrs, "packed_field", "packed_struct")?); let is_enum_ty = field_attributes.iter().filter_map(|a| match *a { @@ -349,8 +419,6 @@ pub fn parse_num(s: &str) -> Result { } } - - pub fn parse_struct(ast: &syn::DeriveInput) -> syn::Result { let attributes = PackStructAttribute::parse_all(&parse_sub_attributes(&ast.attrs, "packed_struct", "packed_field")?); @@ -425,7 +493,7 @@ pub fn parse_struct(ast: &syn::DeriveInput) -> syn::Result { } } - let num_bits: usize = { + let mut num_bits: usize = { if let Some(struct_size_bytes) = struct_size_bytes { struct_size_bytes * 8 } else { @@ -437,6 +505,17 @@ pub fn parse_struct(ast: &syn::DeriveInput) -> syn::Result { } }; + let mut prepend: Option = None; + + attributes.iter().for_each(|a| { + if let PackStructAttribute::Prepend(prep) = a { + if let Prepend::Bytes(bytes) = prep { + num_bits += bytes.len() * 8; + } + prepend = Some(prep.to_owned()); + } + }); + let num_bytes = (num_bits as f32 / 8.0).ceil() as usize; if first_field_is_auto_positioned && (num_bits % 8) != 0 && struct_size_bytes.is_none() { @@ -477,6 +556,7 @@ pub fn parse_struct(ast: &syn::DeriveInput) -> syn::Result { data_struct, fields: fields_parsed, num_bytes, - num_bits + num_bits, + prepend, }) } diff --git a/packed_struct_codegen/src/pack_parse_attributes.rs b/packed_struct_codegen/src/pack_parse_attributes.rs index f44bc82..c44dcdc 100644 --- a/packed_struct_codegen/src/pack_parse_attributes.rs +++ b/packed_struct_codegen/src/pack_parse_attributes.rs @@ -5,7 +5,9 @@ pub enum PackStructAttributeKind { SizeBytes, //SizeBits, DefaultIntEndianness, - BitNumbering + BitNumbering, + Prepend, + Append, } impl PackStructAttributeKind { @@ -16,7 +18,9 @@ impl PackStructAttributeKind { SizeBytes => "size_bytes", //SizeBits => "size_bits", DefaultIntEndianness => "endian", - BitNumbering => "bit_numbering" + BitNumbering => "bit_numbering", + Prepend => "prepend", + Append => "append" } } } @@ -25,26 +29,34 @@ pub enum PackStructAttribute { SizeBytes(usize), //SizeBits(usize), DefaultIntEndianness(IntegerEndianness), - BitNumbering(BitNumbering) + BitNumbering(BitNumbering), + Prepend(Prepend), + // Append(F, usize), } impl PackStructAttribute { - pub fn parse(name: &str, val: &str) -> Result { + pub fn parse(name: &str, val: &syn::Expr) -> Result { if name == PackStructAttributeKind::DefaultIntEndianness.get_attr_name() { + let val = &get_string_from_expr(val).unwrap_or_default(); let v = IntegerEndianness::from_str(val).ok_or_else(|| format!("Invalid default int endian value: {}", val))?; return Ok(PackStructAttribute::DefaultIntEndianness(v)); } if name == PackStructAttributeKind::BitNumbering.get_attr_name() { - let b = BitNumbering::from_str(val).expect("Invalid bit numbering attribute value"); + let b = BitNumbering::from_str(&get_string_from_expr(val).unwrap_or_default()).expect("Invalid bit numbering attribute value"); return Ok(PackStructAttribute::BitNumbering(b)); } if name == PackStructAttributeKind::SizeBytes.get_attr_name() { - let b = parse_num(val)?; + let b = parse_num(&get_string_from_expr(val).unwrap_or_default())?; return Ok(PackStructAttribute::SizeBytes(b)); } + if name == PackStructAttributeKind::Prepend.get_attr_name() { + let b = Prepend::from_expr(val).expect("Invalid prepend value"); + return Ok(PackStructAttribute::Prepend(b)); + } + /* if name == PackStructAttributeKind::SizeBits.get_attr_name() { let b = parse_num(val); @@ -52,10 +64,11 @@ impl PackStructAttribute { } */ + let val = &get_string_from_expr(val).unwrap_or_default(); Err(format!("Unknown struct attribute: {}, value {}", name, val)) } - pub fn parse_all(attributes: &[(String, String)]) -> Vec { + pub fn parse_all(attributes: &[(String, syn::Expr)]) -> Vec { let mut r = vec![]; for (name, val) in attributes { if let Ok(attr) = Self::parse(name, val) { From 6a291162259c08ec617a4be48ebe4648619e135d Mon Sep 17 00:00:00 2001 From: "Jomer.Dev" Date: Fri, 13 Oct 2023 14:29:43 +0200 Subject: [PATCH 2/7] Remove prepend attribute Add header and footer attributes Add header and footer traits Add optional validation through header and footer --- packed_struct/src/packing.rs | 34 ++++- packed_struct_codegen/src/pack.rs | 3 +- packed_struct_codegen/src/pack_codegen.rs | 122 +++++++++++++----- packed_struct_codegen/src/pack_parse.rs | 122 +++++++++++++----- .../src/pack_parse_attributes.rs | 37 ++++-- 5 files changed, 238 insertions(+), 80 deletions(-) diff --git a/packed_struct/src/packing.rs b/packed_struct/src/packing.rs index f38ffa6..6a5fa96 100644 --- a/packed_struct/src/packing.rs +++ b/packed_struct/src/packing.rs @@ -19,6 +19,30 @@ pub trait PackedStruct where Self: Sized { fn unpack(src: &Self::ByteArray) -> PackingResult; } +pub trait PackedStructHeader where Self: PackedStruct + Sized { + /// The appropriately sized byte array into which this structure will be packed, for example [u8; 2]. + type HeaderByteArray : ByteArray; + + /// Returns the header data to attach it to the front of the packed data + fn get_data(&self, data: &[u8]) -> PackingResult; + /// Validates the structure/footer from a byte array when unpacking. + fn validate_data(_src: &[u8]) -> PackingResult<()> { + Ok(()) + } +} + +pub trait PackedStructFooter where Self: PackedStruct + Sized { + /// The appropriately sized byte array into which this structure will be packed, for example [u8; 2]. + type FooterByteArray : ByteArray; + + /// Returns the footer data to attach it to the end of the packed data + fn get_data(&self, data: &[u8]) -> PackingResult; + /// Validates the structure/footer from a byte array when unpacking. + fn validate_data(_src: &[u8]) -> PackingResult<()> { + Ok(()) + } +} + /// Infos about a particular type that can be packaged. pub trait PackedStructInfo { /// Number of bits that this structure occupies when being packed. @@ -44,7 +68,7 @@ pub trait PackedStructSlice where Self: Sized { } #[cfg_attr(feature = "use_serde", derive(Serialize, Deserialize))] -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq, Eq)] /// Packing errors that might occur during packing or unpacking pub enum PackingError { InvalidValue, @@ -56,7 +80,8 @@ pub enum PackingError { BufferSizeMismatch { expected: usize, actual: usize }, BufferModMismatch { actual_size: usize, modulo_required: usize }, SliceIndexingError { slice_len: usize }, - InternalError + InternalError, + UserError(String) } impl crate::Display for PackingError { @@ -68,7 +93,7 @@ impl crate::Display for PackingError { #[cfg(feature="std")] impl ::std::error::Error for PackingError { fn description(&self) -> &str { - match *self { + match self { PackingError::InvalidValue => "Invalid value", PackingError::BitsError => "Bits error", PackingError::BufferTooSmall => "Buffer too small", @@ -78,7 +103,8 @@ impl ::std::error::Error for PackingError { PackingError::BufferModMismatch { .. } => "The structure's size is not a multiple of the item's size", PackingError::SliceIndexingError { .. } => "Failed to index into a slice", PackingError::MoreThanOneDynamicType => "Only one dynamically sized type is supported in the tuple", - PackingError::InternalError => "Internal error" + PackingError::InternalError => "Internal error", + PackingError::UserError(err) => &err } } } diff --git a/packed_struct_codegen/src/pack.rs b/packed_struct_codegen/src/pack.rs index 3612bf5..71aff07 100644 --- a/packed_struct_codegen/src/pack.rs +++ b/packed_struct_codegen/src/pack.rs @@ -50,7 +50,8 @@ pub struct PackStruct<'a> { pub num_bits: usize, pub data_struct: &'a syn::DataStruct, pub derive_input: &'a syn::DeriveInput, - pub prepend: Option, + pub header: Option
, + pub footer: Option