Skip to content

Adds header and footer attributes and traits #106

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 32 additions & 4 deletions packed_struct/src/packing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@ pub trait PackedStruct where Self: Sized {
fn unpack(src: &Self::ByteArray) -> PackingResult<Self>;
}

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_header(&self, data: &[u8]) -> PackingResult<Self::HeaderByteArray>;
/// Validates the structure/footer from a byte array when unpacking.
fn validate_header(_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_footer(&self, data: &[u8]) -> PackingResult<Self::FooterByteArray>;
/// Validates the structure/footer from a byte array when unpacking.
fn validate_footer(_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.
Expand All @@ -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,
Expand All @@ -56,7 +80,9 @@ pub enum PackingError {
BufferSizeMismatch { expected: usize, actual: usize },
BufferModMismatch { actual_size: usize, modulo_required: usize },
SliceIndexingError { slice_len: usize },
InternalError
InternalError,
#[cfg(feature = "std")]
UserError(String)
}

impl crate::Display for PackingError {
Expand All @@ -68,7 +94,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",
Expand All @@ -78,7 +104,9 @@ 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",
#[cfg(feature = "std")]
PackingError::UserError(err) => err
}
}
}
Expand Down
4 changes: 3 additions & 1 deletion packed_struct_codegen/src/pack.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ 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 header: Option<Header>,
pub footer: Option<Footer>,
}


Expand Down
98 changes: 90 additions & 8 deletions packed_struct_codegen/src/pack_codegen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ extern crate syn;
use crate::pack::*;
use crate::pack_codegen_docs::*;
use crate::common::*;
use crate::pack_parse::Footer;
use crate::pack_parse::Header;
use syn::spanned::Spanned;
use crate::utils::*;

Expand All @@ -17,15 +19,92 @@ pub fn derive_pack(parsed: &PackStruct) -> syn::Result<proc_macro2::TokenStream>
let type_documentation = type_docs(parsed);
let num_bytes = parsed.num_bytes;
let num_bits = parsed.num_bits;



let mut pack_fields = vec![];
let mut unpack_fields = vec![];
let mut unpack_struct_set = vec![];

let header = &parsed.header;
let mut header_offset = 0;
let mut header_trait_token_get_data = quote!{};
let mut header_trait_token_validate = quote!{};

if let Some(pre) = header {
match pre {
Header::Trait(size) => {
header_offset = *size;
header_trait_token_get_data = quote! {
{
let bytes = Self::get_header(&self, &target)?;
assert!(#num_bytes >= bytes.len(), "Packed bytes array is smaller than header array even though we added the header length!");
assert!(#size == bytes.len(), "Header size set with attribute is not equal to header array size returned by PackedStructHeader trait!");
target[0..bytes.len()].copy_from_slice(&bytes);
}
};
header_trait_token_validate = quote! {
{
Self::validate_header(src)?;
}
};
},
Header::Bytes(bytes) => {
header_offset = bytes.len();
pack_fields.push(quote! {
{
let byt = vec![#(#bytes),*];
assert!(#num_bytes >= byt.len(), "Packed bytes array is smaller than header array even though we added the header length!");
target[0..byt.len()].copy_from_slice(&byt);
}
});
// We don't need to generate something for unpack here, since we pass an offset to pack_bits unpack ignores any header or footer data
},
}
}

let footer = &parsed.footer;
let mut footer_trait_token_get_data = quote!{};
let mut footer_trait_token_validate = quote!{};

if let Some(app) = footer {
match app {
Footer::Trait(size) => {
let footer_offset = num_bytes - *size;
footer_trait_token_get_data = quote! {
{
let bytes = Self::get_footer(&self, &target)?;
assert!(#num_bytes >= bytes.len(), "Packed bytes array is smaller than footer array even though we added the footer length!");
assert!(#size == bytes.len(), "Footer size set with attribute is not equal to footer array size returned by PackedStructFooter trait!");
target[#footer_offset..].copy_from_slice(&bytes);
}
};
footer_trait_token_validate = quote! {
{
Self::validate_footer(src)?;
}
};
},
Footer::Bytes(bytes) => {
let footer_offset = num_bytes - bytes.len();
pack_fields.push(quote! {
{
let byt = vec![#(#bytes),*];
assert!(#num_bytes >= byt.len(), "Packed bytes array is smaller than footer array even though we added the footer length!");
target[#footer_offset..].copy_from_slice(&byt);
}
});
// We don't need to generate something for unpack here, since we pass an offset to pack_bits unpack ignores any header or footer data
},
}
}

{
// We want the validation for header and footer to happen before we unpack the rest of the fields, since there might be broken data if a validation fails
unpack_fields.push(header_trait_token_validate);
unpack_fields.push(footer_trait_token_validate);

let mut reg = |src: &dyn quote::ToTokens, target: &dyn quote::ToTokens, field: &FieldRegular| -> syn::Result<()> {
let bits = pack_bits(field);
let bits = pack_bits(field, header_offset);

let pack = pack_field(src, field);
let unpack = unpack_field(field)?;
Expand Down Expand Up @@ -79,6 +158,9 @@ pub fn derive_pack(parsed: &PackStruct) -> syn::Result<proc_macro2::TokenStream>
}
}

pack_fields.push(header_trait_token_get_data);
pack_fields.push(footer_trait_token_get_data);

}

let result_ty = result_type();
Expand Down Expand Up @@ -111,7 +193,7 @@ pub fn derive_pack(parsed: &PackStruct) -> syn::Result<proc_macro2::TokenStream>
#[allow(unused_imports, unused_parens)]
fn pack(&self) -> ::packed_struct::PackingResult<Self::ByteArray> {
use ::packed_struct::*;

let mut target = [0 as u8; #num_bytes];

#(#pack_fields)*
Expand Down Expand Up @@ -152,13 +234,13 @@ struct PackBitsCopy {
unpack: proc_macro2::TokenStream
}

fn pack_bits(field: &FieldRegular) -> PackBitsCopy {
fn pack_bits(field: &FieldRegular, header_offset: usize) -> PackBitsCopy {
// memcpy
if (field.bit_range_rust.start % 8) == 0 && (field.bit_range_rust.end % 8) == 0 &&
(field.bit_range_rust.len() % 8) == 0 && field.bit_range_rust.len() >= 8
{
let start = field.bit_range_rust.start / 8;
let end = field.bit_range_rust.end / 8;
let start = field.bit_range_rust.start / 8 + header_offset;
let end = field.bit_range_rust.end / 8 + header_offset;

PackBitsCopy {
pack: quote! {
Expand All @@ -172,8 +254,8 @@ fn pack_bits(field: &FieldRegular) -> PackBitsCopy {
}
} else {
let packed_field_len = (field.bit_width as f32 / 8.0).ceil() as usize;
let start_byte = (field.bit_range_rust.start as f32 / 8.0).floor() as usize;
let shift = ((packed_field_len as isize*8) - (field.bit_width as isize)) - (field.bit_range_rust.start as isize - (start_byte as isize * 8));
let start_byte = (field.bit_range_rust.start as f32 / 8.0).floor() as usize + header_offset;
let shift = ((packed_field_len as isize*8) - (field.bit_width as isize)) - (field.bit_range_rust.start as isize - ((start_byte - header_offset) as isize * 8));

let emit_shift = |s: isize| {
match s {
Expand Down
Loading