jiminy-core 0.17.0

Core systems layer for Jiminy: account layout, zero-copy IO, validation, PDA, sysvar access, math, time checks. Declarative macros for error codes, instruction dispatch, and account uniqueness. no_std, no_alloc, no proc macros, BPF-safe.
Documentation
//! Zero-copy account writer with header awareness.

use hopper_runtime::{ProgramError, Address};

use super::header::HEADER_LEN;

/// Generate `write_$name` methods on AccountWriter for LE integer types.
macro_rules! impl_writer_body {
    ($( $name:ident ($ty:ty), $size:literal; )*) => {
        $(
            #[doc = concat!("Write a ", stringify!($ty), " (LE) to the body at the current position.")]
            #[inline(always)]
            pub fn $name(&mut self, val: $ty) -> Result<(), ProgramError> {
                let abs = self.body_offset + self.body_pos;
                if abs + $size > self.data.len() {
                    return Err(ProgramError::AccountDataTooSmall);
                }
                self.data[abs..abs + $size].copy_from_slice(&val.to_le_bytes());
                self.body_pos += $size;
                Ok(())
            }
        )*
    };
}

/// Zero-copy account writer with header awareness.
///
/// Constructs from a borrowed `&mut [u8]`, writes the header, then
/// exposes sequential typed writes for the body.
pub struct AccountWriter<'a> {
    data: &'a mut [u8],
    body_offset: usize,
    body_pos: usize,
}

impl<'a> AccountWriter<'a> {
    /// Create a new writer, initializing the header.
    ///
    /// Writes discriminator + version + layout_id + zeroed flags/reserved.
    #[inline(always)]
    pub fn new(
        data: &'a mut [u8],
        discriminator: u8,
        version: u8,
        layout_id: &[u8; 8],
    ) -> Result<Self, ProgramError> {
        if data.len() < HEADER_LEN {
            return Err(ProgramError::AccountDataTooSmall);
        }
        data[0] = discriminator;
        data[1] = version;
        data[2] = 0;
        data[3] = 0;
        data[4] = layout_id[0];
        data[5] = layout_id[1];
        data[6] = layout_id[2];
        data[7] = layout_id[3];
        data[8] = layout_id[4];
        data[9] = layout_id[5];
        data[10] = layout_id[6];
        data[11] = layout_id[7];
        data[12] = 0;
        data[13] = 0;
        data[14] = 0;
        data[15] = 0;
        Ok(Self {
            data,
            body_offset: HEADER_LEN,
            body_pos: 0,
        })
    }

    /// Create a writer over existing data without touching the header.
    #[inline(always)]
    pub fn from_existing(data: &'a mut [u8]) -> Result<Self, ProgramError> {
        if data.len() < HEADER_LEN {
            return Err(ProgramError::AccountDataTooSmall);
        }
        Ok(Self {
            data,
            body_offset: HEADER_LEN,
            body_pos: 0,
        })
    }

    /// Set the flags field in the header.
    #[inline(always)]
    pub fn set_flags(&mut self, flags: u16) {
        let bytes = flags.to_le_bytes();
        self.data[2] = bytes[0];
        self.data[3] = bytes[1];
    }

    /// Write a u8 to the body at the current position.
    #[inline(always)]
    pub fn write_u8(&mut self, val: u8) -> Result<(), ProgramError> {
        let abs = self.body_offset + self.body_pos;
        if abs >= self.data.len() {
            return Err(ProgramError::AccountDataTooSmall);
        }
        self.data[abs] = val;
        self.body_pos += 1;
        Ok(())
    }

    // LE integer writes: generated by macro.
    impl_writer_body! {
        write_u16(u16),   2;
        write_u32(u32),   4;
        write_u64(u64),   8;
        write_u128(u128), 16;
        write_i16(i16),   2;
        write_i32(i32),   4;
        write_i64(i64),   8;
        write_i128(i128), 16;
    }

    /// Write a 32-byte address to the body at the current position.
    #[inline(always)]
    pub fn write_address(&mut self, addr: &Address) -> Result<(), ProgramError> {
        let abs = self.body_offset + self.body_pos;
        if abs + 32 > self.data.len() {
            return Err(ProgramError::AccountDataTooSmall);
        }
        self.data[abs..abs + 32].copy_from_slice(addr.as_ref());
        self.body_pos += 32;
        Ok(())
    }

    /// Write a bool to the body at the current position.
    #[inline(always)]
    pub fn write_bool(&mut self, val: bool) -> Result<(), ProgramError> {
        self.write_u8(val as u8)
    }

    /// Write an i8 to the body at the current position.
    #[inline(always)]
    pub fn write_i8(&mut self, val: i8) -> Result<(), ProgramError> {
        self.write_u8(val as u8)
    }

    /// Number of body bytes written so far.
    #[inline(always)]
    pub fn written(&self) -> usize {
        self.body_pos
    }

    /// Get the mutable body slice for direct manipulation.
    #[inline(always)]
    pub fn body_mut(&mut self) -> &mut [u8] {
        &mut self.data[self.body_offset..]
    }

    /// Get the full data slice including header.
    #[inline(always)]
    pub fn raw(&self) -> &[u8] {
        self.data
    }

    /// Write a variable-length byte slice to the body at the current position.
    #[inline(always)]
    pub fn write_bytes(&mut self, bytes: &[u8]) -> Result<(), ProgramError> {
        let abs = self.body_offset + self.body_pos;
        if abs + bytes.len() > self.data.len() {
            return Err(ProgramError::AccountDataTooSmall);
        }
        self.data[abs..abs + bytes.len()].copy_from_slice(bytes);
        self.body_pos += bytes.len();
        Ok(())
    }

    /// Skip `n` bytes in the body without writing. Useful for padding.
    #[inline(always)]
    pub fn skip(&mut self, n: usize) -> Result<(), ProgramError> {
        let abs = self.body_offset + self.body_pos;
        if abs + n > self.data.len() {
            return Err(ProgramError::AccountDataTooSmall);
        }
        self.body_pos += n;
        Ok(())
    }

    /// Current body write position.
    #[inline(always)]
    pub fn position(&self) -> usize {
        self.body_pos
    }
}