kazari 0.0.1

A no_std GUI software stack (Work-in-Progress)
Documentation
use core::mem::size_of_val;

fn align_down(value: usize, align: usize) -> usize {
    value & !(align - 1)
}

fn align_up(value: usize, align: usize) -> usize {
    align_down(value + align - 1, align)
}

#[derive(Debug, PartialEq)]
pub enum BytesWriterError {
    TooShort,
    OutOfBounds,
}

pub struct BytesWriter<'a> {
    bytes: &'a mut [u8],
    current: usize,
}

impl<'a> BytesWriter<'a> {
    pub fn new(bytes: &mut [u8]) -> BytesWriter<'_> {
        BytesWriter { bytes, current: 0 }
    }

    pub fn written_len(&self) -> usize {
        self.current
    }

    pub fn remaining_len(&self) -> usize {
        self.bytes.len() - self.current
    }

    pub fn append_until_alignment(
        &mut self,
        value: u8,
        align: usize,
    ) -> Result<(), BytesWriterError> {
        let next = align_up(self.current, align);
        if next > self.bytes.len() {
            return Err(BytesWriterError::TooShort);
        }

        self.bytes[self.current..next].fill(value);
        self.current = next;
        Ok(())
    }

    pub fn append_bytes(&mut self, value: &[u8]) -> Result<(), BytesWriterError> {
        if self.remaining_len() < value.len() {
            return Err(BytesWriterError::TooShort);
        }

        self.bytes[self.current..self.current + value.len()].copy_from_slice(value);
        self.current += value.len();
        Ok(())
    }

    pub fn append_u8(&mut self, value: u8) -> Result<(), BytesWriterError> {
        if self.remaining_len() < size_of_val(&value) {
            return Err(BytesWriterError::TooShort);
        }

        self.bytes[self.current] = value;
        self.current += 1;
        Ok(())
    }

    pub fn append_le_u16(&mut self, value: u16) -> Result<(), BytesWriterError> {
        if self.remaining_len() < size_of_val(&value) {
            return Err(BytesWriterError::TooShort);
        }

        self.write_le_u16(self.current, value)?;
        self.current += size_of_val(&value);
        Ok(())
    }

    pub fn append_le_u32(&mut self, value: u32) -> Result<(), BytesWriterError> {
        if self.remaining_len() < size_of_val(&value) {
            return Err(BytesWriterError::TooShort);
        }

        self.write_le_u32(self.current, value)?;
        self.current += size_of_val(&value);
        Ok(())
    }

    pub fn append_le_i32(&mut self, value: i32) -> Result<(), BytesWriterError> {
        if self.remaining_len() < size_of_val(&value) {
            return Err(BytesWriterError::TooShort);
        }

        let data = i32::to_le_bytes(value);
        self.bytes[self.current..self.current + data.len()].copy_from_slice(&data[..]);
        self.current += data.len();
        Ok(())
    }

    pub fn write_le_u16(&mut self, offset: usize, value: u16) -> Result<(), BytesWriterError> {
        if offset + size_of_val(&value) > self.bytes.len() {
            return Err(BytesWriterError::OutOfBounds);
        }

        let data = u16::to_le_bytes(value);
        self.bytes[offset..offset + data.len()].copy_from_slice(&data[..]);
        Ok(())
    }

    pub fn write_le_u32(&mut self, offset: usize, value: u32) -> Result<(), BytesWriterError> {
        if offset + size_of_val(&value) > self.bytes.len() {
            return Err(BytesWriterError::OutOfBounds);
        }

        let data = u32::to_le_bytes(value);
        self.bytes[offset..offset + data.len()].copy_from_slice(&data[..]);
        Ok(())
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn append_uint() {
        let mut buf = vec![0; 9];
        let mut writer = BytesWriter::new(&mut buf);
        assert_eq!(writer.append_le_u32(0x12345678), Ok(()));
        assert_eq!(writer.remaining_len(), 5);
        assert_eq!(writer.append_le_u16(0xaabb), Ok(()));
        assert_eq!(writer.remaining_len(), 3);
        assert_eq!(writer.append_bytes(&[0x7, 0x8]), Ok(()));
        assert_eq!(writer.remaining_len(), 1);
        assert_eq!(writer.append_u8(0x9), Ok(()));
        assert_eq!(&buf, &[0x78, 0x56, 0x34, 0x12, 0xbb, 0xaa, 0x7, 0x8, 0x9]);
    }
}