pub const MAGIC: &[u8; 7] = b"neyuki!";
pub const BYTECODE_VERSION: u8 = 3;
pub fn compute_crc32(data: &[u8]) -> u32 {
let mut crc: u32 = 0xFFFF_FFFF;
for &byte in data {
crc ^= byte as u32;
for _ in 0..8 {
if crc & 1 != 0 {
crc = (crc >> 1) ^ 0xEDB8_8320;
} else {
crc >>= 1;
}
}
}
!crc
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BytecodeHeader {
pub version: u8,
pub checksum: u32,
}
impl BytecodeHeader {
pub const HEADER_SIZE: usize = 12;
pub fn new(checksum: u32) -> Self {
Self {
version: BYTECODE_VERSION,
checksum,
}
}
pub fn write_to(&self, buf: &mut Vec<u8>) {
buf.extend_from_slice(MAGIC);
buf.push(self.version);
buf.extend_from_slice(&self.checksum.to_le_bytes());
}
pub fn parse(bytes: &[u8]) -> Result<(Self, usize), String> {
if bytes.len() < Self::HEADER_SIZE {
return Err("bytecode buffer too small to contain header".to_string());
}
if &bytes[0..7] != MAGIC {
return Err("invalid bytecode magic bytes: expected 'neyuki!'".to_string());
}
let version = bytes[7];
if version != BYTECODE_VERSION {
return Err(format!(
"unsupported bytecode version: {} (expected {})",
version, BYTECODE_VERSION
));
}
let checksum = u32::from_le_bytes([bytes[8], bytes[9], bytes[10], bytes[11]]);
Ok((BytecodeHeader { version, checksum }, Self::HEADER_SIZE))
}
}
impl Default for BytecodeHeader {
fn default() -> Self {
Self::new(0)
}
}