use core::convert::TryFrom;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
mod abi;
mod read;
mod write;
pub use abi::{Abi, AbiMismatch, Caps};
pub use read::ReadError;
pub use write::WriteError;
use crate::grain::bytecode::VerifyError;
use crate::grain::pos::Site;
use crate::grain::program::Program;
use crate::grain::vm::Fault;
const MAGIC: [u8; 4] = *b"RGRN";
const VERSION: u16 = 10;
mod root_tag {
pub const LOCAL: u8 = 0x01;
pub const TEMPORARY: u8 = 0x02;
pub const NAMED: u8 = 0x03;
pub const THIS: u8 = 0x04;
}
mod step_tag {
pub const INDEX: u8 = 0x01;
pub const PROPERTY: u8 = 0x02;
pub const METHOD: u8 = 0x03;
}
mod tail_tag {
pub const READ: u8 = 0x01;
pub const ASSIGN: u8 = 0x02;
pub const ASSIGN_OP: u8 = 0x03;
}
#[allow(dead_code)]
mod constant {
pub const UNIT: u8 = 0x00;
pub const FALSE: u8 = 0x01;
pub const TRUE: u8 = 0x02;
pub const INT: u8 = 0x03;
pub const FLOAT: u8 = 0x04;
pub const CHAR: u8 = 0x05;
pub const STRING: u8 = 0x06;
pub const ARRAY: u8 = 0x07;
pub const MAP: u8 = 0x08;
pub const BLOB: u8 = 0x09;
pub const RANGE: u8 = 0x0a;
pub const RANGE_INCLUSIVE: u8 = 0x0b;
pub const DECIMAL: u8 = 0x0c;
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Sidecar {
pub positions: Vec<u8>,
pub chains: Vec<u8>,
pub debug_id: u128,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Stripped {
pub artifact: Vec<u8>,
pub sidecar: Sidecar,
}
impl Sidecar {
#[must_use]
pub fn resolve(&self, trace: &[Fault]) -> Vec<Option<Site>> {
trace.iter().map(|fault| self.site(*fault)).collect()
}
#[must_use]
pub fn site(&self, fault: Fault) -> Option<Site> {
fault
.slot
.and_then(|slot| crate::grain::bytecode::sites::resolve(&self.chains, slot))
.or_else(|| {
u32::try_from(fault.address)
.ok()
.and_then(|address| crate::grain::pos::resolve(&self.positions, address))
})
}
}
pub(crate) fn debug_id(positions: &[u8], chains: &[u8]) -> u128 {
let mut hash = 0x6c62_272e_07bb_0142_62b8_2175_6295_c58d_u128;
for byte in positions.iter().chain(chains) {
hash ^= u128::from(*byte);
hash = hash.wrapping_mul(0x0000_0000_0100_0000_0000_0000_0000_013b);
}
hash
}
impl<'a> Program<'a> {
#[must_use]
pub fn sidecar(&self) -> Sidecar {
Sidecar {
positions: self.positions().to_table(),
chains: crate::grain::bytecode::sites::encode(self.chains()),
debug_id: self.debug_id(),
}
}
pub fn write(&self) -> Result<Vec<u8>, WriteError> {
write::write(self, write::Positions::Keep)
}
pub fn write_stripped(&self) -> Result<Stripped, WriteError> {
let artifact = write::write(self, write::Positions::Strip)?;
Ok(Stripped {
sidecar: self.sidecar(),
artifact,
})
}
pub fn read(bytes: &'a [u8]) -> Result<Self, ReadError> {
read::read(bytes)
}
}
struct Cursor<'a> {
bytes: &'a [u8],
pos: usize,
}
impl<'a> Cursor<'a> {
fn new(bytes: &'a [u8]) -> Self {
Self { bytes, pos: 0 }
}
fn take(&mut self, n: usize) -> Result<&'a [u8], ReadError> {
let end = self.pos.checked_add(n).ok_or(ReadError::Truncated)?;
let slice = self.bytes.get(self.pos..end).ok_or(ReadError::Truncated)?;
self.pos = end;
Ok(slice)
}
fn byte(&mut self) -> Result<u8, ReadError> {
Ok(self.take(1)?[0])
}
fn count(&mut self) -> Result<usize, ReadError> {
let count = usize::try_from(self.uvarint()?).map_err(|_| ReadError::Truncated)?;
if count > self.bytes.len() - self.pos {
return Err(ReadError::Truncated);
}
Ok(count)
}
fn uvarint(&mut self) -> Result<u64, ReadError> {
let mut value = 0u64;
for shift in (0..64).step_by(7) {
let byte = self.byte()?;
let payload = u64::from(byte & 0x7f);
if shift == 63 && payload > 1 {
return Err(ReadError::MalformedVarint);
}
value |= payload << shift;
if byte & 0x80 == 0 {
return Ok(value);
}
}
Err(ReadError::MalformedVarint)
}
fn ivarint(&mut self) -> Result<i64, ReadError> {
let raw = self.uvarint()?;
Ok(((raw >> 1) as i64) ^ -((raw & 1) as i64))
}
fn index(&mut self) -> Result<u32, ReadError> {
u32::try_from(self.uvarint()?).map_err(|_| ReadError::MalformedVarint)
}
fn small(&mut self) -> Result<u16, ReadError> {
u16::try_from(self.uvarint()?).map_err(|_| ReadError::MalformedVarint)
}
fn str(&mut self) -> Result<&'a str, ReadError> {
let len = usize::try_from(self.uvarint()?).map_err(|_| ReadError::Truncated)?;
core::str::from_utf8(self.take(len)?).map_err(|_| ReadError::BadUtf8)
}
fn at_end(&self) -> bool {
self.pos == self.bytes.len()
}
}
fn put_uvarint(out: &mut Vec<u8>, mut value: u64) {
loop {
let byte = (value & 0x7f) as u8;
value >>= 7;
if value == 0 {
out.push(byte);
return;
}
out.push(byte | 0x80);
}
}
fn put_ivarint(out: &mut Vec<u8>, value: i64) {
put_uvarint(out, ((value << 1) ^ (value >> 63)) as u64);
}
fn put_str(out: &mut Vec<u8>, value: &str) {
put_uvarint(out, value.len() as u64);
out.extend_from_slice(value.as_bytes());
}
impl From<VerifyError> for ReadError {
fn from(err: VerifyError) -> Self {
Self::Unverifiable(err)
}
}
impl From<crate::grain::bytecode::BadTable> for ReadError {
fn from(err: crate::grain::bytecode::BadTable) -> Self {
Self::Names(err)
}
}
impl From<crate::grain::bytecode::TableError> for ReadError {
fn from(err: crate::grain::bytecode::TableError) -> Self {
Self::Positions(err)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn unsigned_varints_round_trip_at_the_edges() {
for value in [0u64, 1, 127, 128, 300, u32::MAX as u64, u64::MAX] {
let mut buf = Vec::new();
put_uvarint(&mut buf, value);
assert_eq!(Cursor::new(&buf).uvarint().unwrap(), value, "at {value}");
}
}
#[test]
fn signed_varints_round_trip_across_zero() {
for value in [0i64, -1, 1, -64, 63, i32::MIN as i64, i64::MIN, i64::MAX] {
let mut buf = Vec::new();
put_ivarint(&mut buf, value);
assert_eq!(Cursor::new(&buf).ivarint().unwrap(), value, "at {value}");
}
}
#[test]
fn small_indices_cost_one_byte() {
let mut buf = Vec::new();
put_uvarint(&mut buf, 127);
assert_eq!(buf.len(), 1);
}
#[test]
fn a_run_of_continuation_bytes_terminates() {
let never_ends = vec![0xff_u8; 64];
assert_eq!(
Cursor::new(&never_ends).uvarint(),
Err(ReadError::MalformedVarint),
);
}
#[test]
fn a_tenth_group_wider_than_one_bit_is_refused() {
let mut ten = [0x80u8; 10];
let widest = [[0xff_u8; 9].as_slice(), &[0x01]].concat();
assert_eq!(Cursor::new(&widest).uvarint(), Ok(u64::MAX));
ten[9] = 0x02;
assert_eq!(Cursor::new(&ten).uvarint(), Err(ReadError::MalformedVarint));
ten[9] = 0x7f;
assert_eq!(Cursor::new(&ten).uvarint(), Err(ReadError::MalformedVarint));
}
#[test]
fn reading_past_the_end_is_an_error_not_a_panic() {
assert_eq!(Cursor::new(&[]).byte(), Err(ReadError::Truncated));
assert_eq!(Cursor::new(&[1, 2]).take(9), Err(ReadError::Truncated));
}
}