vyre 0.1.0

GPU bytecode condition engine
Documentation
use crate::error::{Error, Result};

const MAGIC: [u8; 8] = *b"VYREPART";

/// Frame multiple byte slices into a single blob with length prefixes.
pub fn encode_parts(parts: &[&[u8]]) -> Vec<u8> {
    let total = MAGIC.len() + parts.iter().map(|part| 8 + part.len()).sum::<usize>();
    let mut out = Vec::with_capacity(total);
    out.extend_from_slice(&MAGIC);
    for part in parts {
        out.extend_from_slice(&(part.len() as u64).to_le_bytes());
        out.extend_from_slice(part);
    }
    out
}

/// Decode a blob produced by [`encode_parts`].
pub fn decode_parts(mut bytes: &[u8]) -> Result<Vec<&[u8]>> {
    if bytes.len() < MAGIC.len() || bytes[..MAGIC.len()] != MAGIC {
        return Err(Error::Gpu {
            message: "invalid vyre serializer header".to_string(),
        });
    }
    bytes = &bytes[MAGIC.len()..];
    let mut parts = Vec::new();
    while !bytes.is_empty() {
        if bytes.len() < 8 {
            return Err(Error::Gpu {
                message: "truncated framed part length".to_string(),
            });
        }
        let len = u64::from_le_bytes(bytes[..8].try_into().map_err(|_| Error::Gpu {
            message: "invalid framed part length".to_string(),
        })?) as usize;
        bytes = &bytes[8..];
        if bytes.len() < len {
            return Err(Error::Gpu {
                message: "truncated framed part payload".to_string(),
            });
        }
        let (part, rest) = bytes.split_at(len);
        parts.push(part);
        bytes = rest;
    }
    Ok(parts)
}