use crate::*;
use brec_consts::*;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
impl Read for Block {
fn generate(&self) -> Result<TokenStream, E> {
let block_name = self.name();
let const_sig = self.const_sig_name();
let mut fields = Vec::new();
let mut fnames = Vec::new();
let sig_len = self.sig_len();
let src: syn::Ident = format_ident!("buf");
let read_len = if cfg!(feature = "resilient") {
quote! {
let mut blk_len = [0u8; #BLOCK_SIZE_FIELD_LEN];
#src.read_exact(&mut blk_len)?;
let blk_len = u32::from_le_bytes(blk_len);
let expected_len = #block_name::ssize()
.checked_sub(#sig_len)
.and_then(|len| len.checked_sub(#BLOCK_SIZE_FIELD_LEN as u64))
.and_then(|len| len.checked_sub(#BLOCK_CRC_LEN as u64))
.and_then(|len| u32::try_from(len).ok())
.ok_or(brec::Error::InvalidLength)?;
if blk_len != expected_len {
return Err(brec::Error::InvalidLength);
}
}
} else {
quote! {}
};
for field in self.fields.iter().filter(|f| !f.injected) {
fields.push(field.read_exact(&src)?);
fnames.push(format_ident!("{}", field.name));
}
Ok(quote! {
impl brec::ReadBlockFrom for #block_name {
fn read<T: std::io::Read>(buf: &mut T, skip_sig: bool) -> Result<Self, brec::Error>
where
Self: Sized {
use brec::prelude::*;
if !skip_sig {
let mut sig = [0u8; #sig_len];
#src.read_exact(&mut sig)?;
if sig != #const_sig {
return Err(brec::Error::SignatureDismatch(brec::Unrecognized::block(sig)))
}
}
#read_len
#(#fields)*
let mut crc = [0u8; 4];
#src.read_exact(&mut crc)?;
let block = #block_name {
#(#fnames,)*
};
if block.crc() != crc {
return Err(brec::Error::CrcDismatch)
}
Ok(block)
}
}
})
}
}
impl ReadFromSlice for Block {
fn generate(&self) -> Result<TokenStream, E> {
let referred_name = self.referred_name();
let block_name = self.name();
let const_sig = self.const_sig_name();
let sig_len = self.sig_len();
let mut fields = Vec::new();
let mut fnames = Vec::new();
let mut offset = 0usize;
let src: syn::Ident = format_ident!("buf");
let read_len = if cfg!(feature = "resilient") {
quote! {
let required = if skip_sig { 0usize } else { #sig_len } + #BLOCK_SIZE_FIELD_LEN;
if #src.len() < required {
return Err(brec::Error::NotEnoughData(required - #src.len()));
}
let len_from = if skip_sig { 0usize } else { #sig_len };
let len_to = len_from + #BLOCK_SIZE_FIELD_LEN;
let blk_len =
u32::from_le_bytes(<[u8; #BLOCK_SIZE_FIELD_LEN]>::try_from(&#src[len_from..len_to])?);
let expected_len = #block_name::ssize()
.checked_sub(#sig_len)
.and_then(|len| len.checked_sub(#BLOCK_SIZE_FIELD_LEN as u64))
.and_then(|len| len.checked_sub(#BLOCK_CRC_LEN as u64))
.and_then(|len| u32::try_from(len).ok())
.ok_or(brec::Error::InvalidLength)?;
if blk_len != expected_len {
return Err(brec::Error::InvalidLength);
}
}
} else {
quote! {}
};
for field in self.fields.iter() {
if field.name == FIELD_SIG {
let name = format_ident!("{}", FIELD_SIG);
fields.push(quote! {
let #name = if skip_sig {
&#const_sig
} else {
<&[u8; #BLOCK_SIG_LEN]>::try_from(&#src[0usize..#BLOCK_SIG_LEN])?
};
});
#[cfg(feature = "resilient")]
{
offset += BLOCK_SIZE_FIELD_LEN;
}
} else if field.name == FIELD_CRC {
let name = format_ident!("{}", FIELD_CRC);
fields.push(quote! {
let #name = <&[u8; #BLOCK_CRC_LEN]>::try_from(&#src[#offset..#offset + #BLOCK_CRC_LEN])?;
let crc = #name;
});
} else {
fields.push(field.safe(&src, offset, offset + field.ty.size()));
}
fnames.push(format_ident!("{}", field.name));
offset += field.ty.size();
}
Ok(quote! {
impl<'a> brec::ReadBlockFromSlice for #referred_name <'a> {
fn read_from_slice<'b>(#src: &'b [u8], skip_sig: bool) -> Result<Self, brec::Error>
where
Self: 'b + Sized,
{
use brec::prelude::*;
if !skip_sig {
if #src.len() < #sig_len {
return Err(brec::Error::NotEnoughtSignatureData(#src.len(), #sig_len));
}
if #src[..#sig_len] != #const_sig {
return Err(brec::Error::SignatureDismatch(
brec::Unrecognized::block(<[u8; #sig_len]>::try_from(&#src[..#sig_len])?),
));
}
}
#read_len
let required = if skip_sig {
#block_name::ssize() - #sig_len
} else {
#block_name::ssize()
} as usize;
if #src.len() < required {
return Err(brec::Error::NotEnoughData(required - #src.len()));
}
#(#fields)*
let block:#referred_name <'b> = #referred_name {
#(#fnames,)*
};
if block.crc() != *crc {
return Err(brec::Error::CrcDismatch)
}
let block: #referred_name <'a> = unsafe { std::mem::transmute(block) };
Ok(block)
}
}
})
}
}
impl TryRead for Block {
fn generate(&self) -> Result<TokenStream, E> {
let block_name = self.name();
let const_sig = self.const_sig_name();
let sig_len = self.sig_len();
Ok(quote! {
impl brec::TryReadFrom for #block_name {
fn try_read<T: std::io::Read + std::io::Seek>(buf: &mut T) -> Result<brec::ReadStatus<Self>, brec::Error> {
use brec::prelude::*;
let mut sig_buf = [0u8; #sig_len];
let start_pos = buf.stream_position()?;
let len = buf.seek(std::io::SeekFrom::End(0))? - start_pos;
buf.seek(std::io::SeekFrom::Start(start_pos))?;
if len < #sig_len {
return Ok(brec::ReadStatus::NotEnoughData(#sig_len - len));
}
buf.read_exact(&mut sig_buf)?;
if sig_buf != #const_sig {
buf.seek(std::io::SeekFrom::Start(start_pos))?;
return Err(brec::Error::SignatureDismatch(brec::Unrecognized::block(sig_buf)));
}
if len < #block_name::ssize() {
buf.seek(std::io::SeekFrom::Start(start_pos))?;
return Ok(brec::ReadStatus::NotEnoughData(#block_name::ssize() - len));
}
Ok(brec::ReadStatus::Success(#block_name::read(buf, true)?))
}
}
})
}
}
impl TryReadBuffered for Block {
fn generate(&self) -> Result<TokenStream, E> {
let block_name = self.name();
let const_sig = self.const_sig_name();
let sig_len = self.sig_len();
Ok(quote! {
impl brec::TryReadFromBuffered for #block_name {
fn try_read<T: std::io::BufRead>(reader: &mut T) -> Result<brec::ReadStatus<Self>, brec::Error> {
use std::io::BufRead;
use brec::prelude::*;
let bytes = reader.fill_buf()?;
if bytes.len() < #sig_len {
return Ok(brec::ReadStatus::NotEnoughData(
(#sig_len - bytes.len()) as u64,
));
}
if !bytes.starts_with(&#const_sig) {
return Err(brec::Error::SignatureDismatch(brec::Unrecognized::block(
<[u8; #sig_len]>::try_from(&bytes[..#sig_len])?,
)));
}
reader.consume(#sig_len);
let blk = #block_name::read(reader, true);
Ok(brec::ReadStatus::Success(blk?))
}
}
})
}
}