use alloc::vec::Vec;
use crate::lower::RegModule;
pub const AOT_MAGIC: [u8; 7] = *b"BDKAOT1";
const ENVELOPE_LEN: usize = AOT_MAGIC.len() + 4;
pub const AOT_FORMAT_VERSION: u32 = 1;
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum AotError {
TooShort,
BadMagic,
UnsupportedVersion {
found: u32,
expected: u32,
},
MalformedPayload,
}
impl core::fmt::Display for AotError {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Self::TooShort => write!(f, "artifact shorter than the envelope"),
Self::BadMagic => write!(f, "artifact magic mismatch"),
Self::UnsupportedVersion { found, expected } => write!(
f,
"artifact format version {found} (this runtime understands {expected})"
),
Self::MalformedPayload => write!(f, "artifact payload failed to decode"),
}
}
}
impl core::error::Error for AotError {}
pub fn serialize(module: &RegModule) -> Vec<u8> {
let payload = postcard::to_allocvec(module).expect("RegModule serialization is infallible");
let mut out = Vec::with_capacity(ENVELOPE_LEN + payload.len());
out.extend_from_slice(&AOT_MAGIC);
out.extend_from_slice(&AOT_FORMAT_VERSION.to_le_bytes());
out.extend_from_slice(&payload);
out
}
pub fn deserialize(bytes: &[u8]) -> Result<RegModule, AotError> {
if bytes.len() < ENVELOPE_LEN {
return Err(AotError::TooShort);
}
if bytes[..AOT_MAGIC.len()] != AOT_MAGIC {
return Err(AotError::BadMagic);
}
let version = u32::from_le_bytes(
bytes[AOT_MAGIC.len()..ENVELOPE_LEN]
.try_into()
.expect("envelope slice length checked above"),
);
if version != AOT_FORMAT_VERSION {
return Err(AotError::UnsupportedVersion {
found: version,
expected: AOT_FORMAT_VERSION,
});
}
postcard::from_bytes(&bytes[ENVELOPE_LEN..]).map_err(|_| AotError::MalformedPayload)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::binary::module::Module;
fn tiny_module() -> RegModule {
let buf = wast::parser::ParseBuffer::new(
"(module (func (export \"add\") (param i32 i32) (result i32)\n local.get 0 local.get 1 i32.add))",
)
.unwrap();
let mut wat = wast::parser::parse::<wast::Wat<'_>>(&buf).unwrap();
let bytes = wat.encode().unwrap();
Module::decode(&bytes).unwrap().lower().unwrap()
}
#[test]
fn roundtrip_preserves_module() {
let module = tiny_module();
let artifact = serialize(&module);
assert_eq!(&artifact[..AOT_MAGIC.len()], &AOT_MAGIC);
assert_eq!(deserialize(&artifact), Ok(module));
}
#[test]
fn rejects_bad_envelope() {
let module = tiny_module();
let artifact = serialize(&module);
assert_eq!(deserialize(&artifact[..4]), Err(AotError::TooShort));
let mut bad_magic = artifact.clone();
bad_magic[0] = b'X';
assert_eq!(deserialize(&bad_magic), Err(AotError::BadMagic));
let mut bad_version = artifact.clone();
bad_version[AOT_MAGIC.len()] = 0xff;
assert_eq!(
deserialize(&bad_version),
Err(AotError::UnsupportedVersion {
found: 0xff,
expected: AOT_FORMAT_VERSION,
})
);
let truncated = &artifact[..artifact.len() - 3];
assert_eq!(deserialize(truncated), Err(AotError::MalformedPayload));
}
}