#![no_std]
use core::mem::{offset_of, size_of};
pub const TOCAT_ABI_VERSION: u32 = 1;
pub const TOCAT_OUTBOX_LEN: u32 = 48;
pub const TOCAT_LOG_RECORD_LEN: u32 = 12;
pub const TOCAT_EMIT_PENDING: u32 = 0;
pub const TOCAT_EMIT_PASSTHROUGH: u32 = 1;
pub const TOCAT_EMIT_BUFFERED: u32 = 2;
pub const TOCAT_FLAG_REARM: u32 = 1 << 0;
pub const TOCAT_FLAG_HALT: u32 = 1 << 1;
pub const TOCAT_FLAG_PACE: u32 = 1 << 2;
pub const TOCAT_FLAG_ERROR: u32 = 1 << 3;
pub const TOCAT_TRACE: u32 = 0;
pub const TOCAT_DEBUG: u32 = 1;
pub const TOCAT_INFO: u32 = 2;
pub const TOCAT_WARN: u32 = 3;
pub const TOCAT_ERROR: u32 = 4;
#[repr(C)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct LogRecord {
pub level: u32,
pub ptr: u32,
pub len: u32,
}
#[repr(C)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub struct Outbox {
pub emit: u32,
pub bytes_ptr: u32,
pub bytes_len: u32,
pub bounds_ptr: u32,
pub bounds_len: u32,
pub flags: u32,
pub message_ptr: u32,
pub message_len: u32,
pub pace_ns: u64,
pub logs_ptr: u32,
pub logs_len: u32,
}
const _: () = {
assert!(size_of::<Outbox>() == TOCAT_OUTBOX_LEN as usize);
assert!(offset_of!(Outbox, emit) == 0);
assert!(offset_of!(Outbox, bytes_ptr) == 4);
assert!(offset_of!(Outbox, bytes_len) == 8);
assert!(offset_of!(Outbox, bounds_ptr) == 12);
assert!(offset_of!(Outbox, bounds_len) == 16);
assert!(offset_of!(Outbox, flags) == 20);
assert!(offset_of!(Outbox, message_ptr) == 24);
assert!(offset_of!(Outbox, message_len) == 28);
assert!(offset_of!(Outbox, pace_ns) == 32);
assert!(offset_of!(Outbox, logs_ptr) == 40);
assert!(offset_of!(Outbox, logs_len) == 44);
assert!(size_of::<LogRecord>() == TOCAT_LOG_RECORD_LEN as usize);
};
impl Outbox {
pub const fn new() -> Self {
Self {
emit: TOCAT_EMIT_PENDING,
bytes_ptr: 0,
bytes_len: 0,
bounds_ptr: 0,
bounds_len: 0,
flags: 0,
message_ptr: 0,
message_len: 0,
pace_ns: 0,
logs_ptr: 0,
logs_len: 0,
}
}
pub fn reset(&mut self) {
*self = Self::new();
}
pub const fn emit(&self) -> Option<Emit> {
Emit::from_u32(self.emit)
}
pub const fn set_emit(&mut self, emit: Emit) {
self.emit = emit.as_u32();
}
pub const fn has(&self, flag: u32) -> bool {
self.flags & flag != 0
}
pub const fn set(&mut self, flag: u32) {
self.flags |= flag;
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
pub enum Emit {
#[default]
Pending = TOCAT_EMIT_PENDING,
Passthrough = TOCAT_EMIT_PASSTHROUGH,
Buffered = TOCAT_EMIT_BUFFERED,
}
impl Emit {
pub const fn from_u32(value: u32) -> Option<Self> {
match value {
TOCAT_EMIT_PENDING => Some(Self::Pending),
TOCAT_EMIT_PASSTHROUGH => Some(Self::Passthrough),
TOCAT_EMIT_BUFFERED => Some(Self::Buffered),
_ => None,
}
}
pub const fn as_u32(self) -> u32 {
self as u32
}
}
#[repr(u32)]
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
pub enum Level {
Trace = TOCAT_TRACE,
Debug = TOCAT_DEBUG,
#[default]
Info = TOCAT_INFO,
Warn = TOCAT_WARN,
Error = TOCAT_ERROR,
}
impl Level {
pub const fn from_u32(value: u32) -> Self {
match value {
TOCAT_TRACE => Self::Trace,
TOCAT_DEBUG => Self::Debug,
TOCAT_WARN => Self::Warn,
TOCAT_ERROR => Self::Error,
_ => Self::Info,
}
}
pub const fn as_u32(self) -> u32 {
self as u32
}
}
pub mod exports {
pub const MEMORY: &str = "memory";
pub const ABI_VERSION: &str = "tocat_abi_version";
pub const OUTBOX: &str = "tocat_outbox";
pub const ALLOC: &str = "tocat_alloc";
pub const INIT: &str = "tocat_init";
pub const ON_BYTES: &str = "tocat_on_bytes";
pub const ON_EOF: &str = "tocat_on_eof";
pub const ON_TICK: &str = "tocat_on_tick";
pub const TICK_INTERVAL_NS: &str = "tocat_tick_interval_ns";
pub const DATAGRAM_SAFE: &str = "tocat_datagram_safe";
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn the_enums_are_the_constants() {
assert_eq!(Emit::Buffered.as_u32(), TOCAT_EMIT_BUFFERED);
assert_eq!(Level::Warn.as_u32(), TOCAT_WARN);
assert_eq!(
Emit::from_u32(TOCAT_EMIT_PASSTHROUGH),
Some(Emit::Passthrough)
);
assert_eq!(Emit::from_u32(3), None);
assert_eq!(Level::from_u32(99), Level::Info);
}
#[test]
fn an_outbox_starts_and_resets_empty() {
let mut outbox = Outbox::new();
assert_eq!(outbox.emit(), Some(Emit::Pending));
outbox.set(TOCAT_FLAG_HALT);
outbox.set_emit(Emit::Buffered);
assert!(outbox.has(TOCAT_FLAG_HALT));
outbox.reset();
assert_eq!(outbox, Outbox::new());
assert!(!outbox.has(TOCAT_FLAG_HALT));
}
}