use super::*;
#[cfg(not(feature = "std"))]
use alloc::{
format,
string::{String, ToString},
vec::Vec,
};
pub trait WriteWithNames: WriteWithPos + Sized {
fn align<V: AlignTo>(&mut self) -> Result<()> {
let padding = pad_align_to(self.pos(), V::align_to());
for _ in 0..padding {
self.write_all(&[0])?;
}
Ok(())
}
fn write<V: SerInner>(&mut self, _field_name: &str, value: &V) -> Result<()> {
unsafe { value._ser_inner(self) }
}
fn write_bytes<V: SerInner + ZeroCopy>(&mut self, value: &[u8]) -> Result<()> {
self.write_all(value)
}
}
impl<F: WriteNoStd> WriteWithNames for WriterWithPos<'_, F> {}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg, mem_dbg::MemSize))]
pub struct SchemaRow {
pub field: String,
pub ty: String,
pub offset: usize,
pub size: usize,
pub align: usize,
}
#[derive(Default, Debug, Clone)]
#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg, mem_dbg::MemSize))]
pub struct Schema(pub Vec<SchemaRow>);
impl Schema {
pub fn debug(&self, data: &[u8]) -> String {
let mut result = "field,offset,align,size,ty,bytes\n".to_string();
for i in 0..self.0.len().saturating_sub(1) {
let row = &self.0[i];
if row.offset == self.0[i + 1].offset {
result.push_str(&format!(
"{},{},{},{},{},\n",
row.field, row.offset, row.align, row.size, row.ty,
));
} else {
result.push_str(&format!(
"{},{},{},{},{},{:02x?}\n",
row.field,
row.offset,
row.align,
row.size,
row.ty,
&data[row.offset..row.offset + row.size],
));
}
}
if let Some(row) = self.0.last() {
result.push_str(&format!(
"{},{},{},{},{},{:02x?}\n",
row.field,
row.offset,
row.align,
row.size,
row.ty,
&data[row.offset..row.offset + row.size],
));
}
result
}
pub fn to_csv(&self) -> String {
let mut result = "field,offset,align,size,ty\n".to_string();
for row in &self.0 {
result.push_str(&format!(
"{},{},{},{},{}\n",
row.field, row.offset, row.align, row.size, row.ty
));
}
result
}
}
#[derive(Debug)]
#[cfg_attr(feature = "mem_dbg", derive(mem_dbg::MemDbg, mem_dbg::MemSize))]
pub struct SchemaWriter<'a, W> {
pub schema: Schema,
path: Vec<String>,
writer: &'a mut W,
}
impl<'a, W: WriteWithPos> SchemaWriter<'a, W> {
pub fn new(backend: &'a mut W) -> Self {
Self {
schema: Default::default(),
path: Vec::new(),
writer: backend,
}
}
}
impl<W: WriteNoStd> WriteNoStd for SchemaWriter<'_, W> {
fn write_all(&mut self, buf: &[u8]) -> ser::Result<()> {
self.writer.write_all(buf)
}
fn flush(&mut self) -> ser::Result<()> {
self.writer.flush()
}
}
impl<W: WriteWithPos> WriteWithPos for SchemaWriter<'_, W> {
fn pos(&self) -> usize {
self.writer.pos()
}
}
impl<W: WriteWithPos> WriteWithNames for SchemaWriter<'_, W> {
fn align<T: AlignTo>(&mut self) -> Result<()> {
let padding = pad_align_to(self.pos(), T::align_to());
if padding != 0 {
self.schema.0.push(SchemaRow {
field: "PADDING".into(),
ty: format!("[u8; {}]", padding),
offset: self.pos(),
size: padding,
align: 1,
});
for _ in 0..padding {
self.write_all(&[0])?;
}
}
Ok(())
}
fn write<V: SerInner>(&mut self, field_name: &str, value: &V) -> Result<()> {
self.path.push(field_name.into());
let pos = self.pos();
let len = self.schema.0.len();
unsafe { value._ser_inner(self)? };
self.schema.0.insert(
len,
SchemaRow {
field: self.path.join("."),
ty: core::any::type_name::<V>().to_string(),
offset: pos,
align: 0,
size: self.pos() - pos,
},
);
self.path.pop();
Ok(())
}
fn write_bytes<V: SerInner + ZeroCopy>(&mut self, value: &[u8]) -> Result<()> {
self.path.push("zero".to_string());
self.schema.0.push(SchemaRow {
field: self.path.join("."),
ty: core::any::type_name::<V>().to_string(),
offset: self.pos(),
size: value.len(),
align: V::align_to(),
});
self.path.pop();
self.write_all(value)
}
}