oxfmt 1.0.0

Small binary serialization and deserialization library
Documentation
use std::any::Any;

use anyhow::{bail, Result};

use crate::types::{Deserializable, Field};

pub trait Readable: Sized {
    fn read(reader: &mut Deserialize) -> Result<Self>;
}

impl Readable for String {
    fn read(reader: &mut Deserialize) -> Result<Self> {
        match reader.buf.iter().position(|&byte| byte == 0) {
            Some(pos) => {
                let string = str::from_utf8(&reader.buf[..pos])?.to_string();
                reader.advance(pos + 1);
                Ok(string)
            }
            None => {
                bail!("unterminated c-string")
            }
        }
    }
}

impl Readable for u8 {
    fn read(reader: &mut Deserialize) -> Result<Self> {
        let bytes = reader.read_bytes(1)?;
        Ok(u8::from_le_bytes(bytes.try_into()?))
    }
}

impl Readable for u16 {
    fn read(reader: &mut Deserialize) -> Result<Self> {
        let bytes = reader.read_bytes(2)?;
        Ok(u16::from_le_bytes(bytes.try_into()?))
    }
}

impl Readable for u32 {
    fn read(reader: &mut Deserialize) -> Result<Self> {
        let bytes = reader.read_bytes(4)?;
        Ok(u32::from_le_bytes(bytes.try_into()?))
    }
}

impl Readable for u64 {
    fn read(reader: &mut Deserialize) -> Result<Self> {
        let bytes = reader.read_bytes(8)?;
        Ok(u64::from_le_bytes(bytes.try_into()?))
    }
}

impl Readable for u128 {
    fn read(reader: &mut Deserialize) -> Result<Self> {
        let bytes = reader.read_bytes(16)?;
        Ok(u128::from_le_bytes(bytes.try_into()?))
    }
}

impl<T: Deserializable> Readable for T {
    fn read(reader: &mut Deserialize) -> Result<Self> {
        let structure = T::get_structure();

        let mut fields = Vec::new();

        for i in 0..structure.fields.len() {
            let field: Box<dyn Any> = match structure.fields.get(&i) {
                Some(field) => match field {
                    Field::String => Box::new(reader.read::<String>()?),
                    Field::U8 => Box::new(reader.read::<u8>()?),
                    Field::U16 => Box::new(reader.read::<u16>()?),
                    Field::U32 => Box::new(reader.read::<u32>()?),
                    Field::U64 => Box::new(reader.read::<u64>()?),
                    Field::U128 => Box::new(reader.read::<u128>()?),
                    Field::Struct => Box::new(reader.read::<T>()?),
                    Field::Vector => Box::new(reader.read::<Vec<T>>()?),
                },
                None => bail!("field index {} not found", i),
            };
            fields.push(field);
        }

        T::construct(fields)
    }
}

impl<T: Readable> Readable for Vec<T> {
    fn read(reader: &mut Deserialize) -> Result<Self> {
        let size = reader.read::<usize>()?;
        let mut vec: Vec<T> = Vec::with_capacity(size);

        for _ in 0..size {
            vec.push(reader.read::<T>()?);
        }

        Ok(vec)
    }
}

impl Readable for usize {
    fn read(reader: &mut Deserialize) -> Result<Self> {
        let size = match reader.arch {
            Some(arch) => match arch {
                0 => 4, // 32-bit
                1 => 8, // 64-bit
                _ => bail!("invalid architecture {}", arch),
            },
            None => bail!("no architecture specified"),
        };

        let bytes = reader.read_bytes(size)?;

        let value = match size {
            4 => {
                let arr: [u8; 4] = bytes.try_into()?;
                u32::from_le_bytes(arr) as u64
            }
            8 => {
                let arr: [u8; 8] = bytes.try_into()?;
                u64::from_le_bytes(arr)
            }
            _ => bail!("unsupported size: {}", size),
        };

        let usize_val =
            usize::try_from(value).map_err(|_| anyhow::anyhow!("value does not fit in usize"))?;

        if usize::BITS < 64 && value > u32::MAX as u64 {
            bail!("value does not fit in 32-bit usize");
        }

        Ok(usize_val)
    }
}

pub struct Deserialize<'a> {
    buf: &'a [u8],
    arch: Option<u8>,
}

impl<'a> Deserialize<'a> {
    pub fn new(buf: &'a [u8], header: &[u8], version: u16) -> Result<Self> {
        Self { buf, arch: None }.init(header, version)
    }

    pub fn read<T: Readable>(&mut self) -> Result<T> {
        T::read(self)
    }

    pub fn read_bytes(&mut self, bytes: usize) -> Result<&[u8]> {
        if self.buf.len() < bytes {
            bail!("not enough bytes")
        }
        let result = &self.buf[..bytes];
        self.advance(bytes);
        Ok(result)
    }

    fn advance(&mut self, len: usize) {
        self.buf = &self.buf[len..];
    }

    fn assert_header(&mut self, header: &[u8]) -> Result<()> {
        if !self.buf.starts_with(header) {
            bail!("buffer does not start with header")
        }
        self.advance(header.len());
        Ok(())
    }

    fn assert_version(&mut self, version: u16) -> Result<()> {
        if self.read::<u16>()? != version {
            bail!("version does not match")
        }
        Ok(())
    }

    fn read_arch(&mut self) -> Result<()> {
        self.arch = Some(self.read::<u8>()?);
        Ok(())
    }

    fn init(mut self, header: &[u8], version: u16) -> Result<Self> {
        self.assert_header(header)?;
        self.assert_version(version)?;
        self.read_arch()?;
        Ok(self)
    }
}