1#![no_std]
40
41use core::mem::{offset_of, size_of};
42
43pub const TOCAT_ABI_VERSION: u32 = 1;
46
47pub const TOCAT_OUTBOX_LEN: u32 = 48;
49
50pub const TOCAT_LOG_RECORD_LEN: u32 = 12;
52
53pub const TOCAT_EMIT_PENDING: u32 = 0;
56pub const TOCAT_EMIT_PASSTHROUGH: u32 = 1;
59pub const TOCAT_EMIT_BUFFERED: u32 = 2;
61
62pub const TOCAT_FLAG_REARM: u32 = 1 << 0;
64pub const TOCAT_FLAG_HALT: u32 = 1 << 1;
66pub const TOCAT_FLAG_PACE: u32 = 1 << 2;
68pub const TOCAT_FLAG_ERROR: u32 = 1 << 3;
70
71pub const TOCAT_TRACE: u32 = 0;
72pub const TOCAT_DEBUG: u32 = 1;
73pub const TOCAT_INFO: u32 = 2;
74pub const TOCAT_WARN: u32 = 3;
75pub const TOCAT_ERROR: u32 = 4;
76
77#[repr(C)]
79#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
80pub struct LogRecord {
81 pub level: u32,
82 pub ptr: u32,
83 pub len: u32,
84}
85
86#[repr(C)]
95#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
96pub struct Outbox {
97 pub emit: u32,
98 pub bytes_ptr: u32,
99 pub bytes_len: u32,
100 pub bounds_ptr: u32,
101 pub bounds_len: u32,
102 pub flags: u32,
103 pub message_ptr: u32,
104 pub message_len: u32,
105 pub pace_ns: u64,
106 pub logs_ptr: u32,
107 pub logs_len: u32,
108}
109
110const _: () = {
111 assert!(size_of::<Outbox>() == TOCAT_OUTBOX_LEN as usize);
112 assert!(offset_of!(Outbox, emit) == 0);
113 assert!(offset_of!(Outbox, bytes_ptr) == 4);
114 assert!(offset_of!(Outbox, bytes_len) == 8);
115 assert!(offset_of!(Outbox, bounds_ptr) == 12);
116 assert!(offset_of!(Outbox, bounds_len) == 16);
117 assert!(offset_of!(Outbox, flags) == 20);
118 assert!(offset_of!(Outbox, message_ptr) == 24);
119 assert!(offset_of!(Outbox, message_len) == 28);
120 assert!(offset_of!(Outbox, pace_ns) == 32);
121 assert!(offset_of!(Outbox, logs_ptr) == 40);
122 assert!(offset_of!(Outbox, logs_len) == 44);
123
124 assert!(size_of::<LogRecord>() == TOCAT_LOG_RECORD_LEN as usize);
125};
126
127impl Outbox {
128 pub const fn new() -> Self {
129 Self {
130 emit: TOCAT_EMIT_PENDING,
131 bytes_ptr: 0,
132 bytes_len: 0,
133 bounds_ptr: 0,
134 bounds_len: 0,
135 flags: 0,
136 message_ptr: 0,
137 message_len: 0,
138 pace_ns: 0,
139 logs_ptr: 0,
140 logs_len: 0,
141 }
142 }
143
144 pub fn reset(&mut self) {
147 *self = Self::new();
148 }
149
150 pub const fn emit(&self) -> Option<Emit> {
151 Emit::from_u32(self.emit)
152 }
153
154 pub const fn set_emit(&mut self, emit: Emit) {
155 self.emit = emit.as_u32();
156 }
157
158 pub const fn has(&self, flag: u32) -> bool {
159 self.flags & flag != 0
160 }
161
162 pub const fn set(&mut self, flag: u32) {
163 self.flags |= flag;
164 }
165}
166
167#[repr(u32)]
169#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
170pub enum Emit {
171 #[default]
173 Pending = TOCAT_EMIT_PENDING,
174 Passthrough = TOCAT_EMIT_PASSTHROUGH,
177 Buffered = TOCAT_EMIT_BUFFERED,
180}
181
182impl Emit {
183 pub const fn from_u32(value: u32) -> Option<Self> {
184 match value {
185 TOCAT_EMIT_PENDING => Some(Self::Pending),
186 TOCAT_EMIT_PASSTHROUGH => Some(Self::Passthrough),
187 TOCAT_EMIT_BUFFERED => Some(Self::Buffered),
188 _ => None,
189 }
190 }
191
192 pub const fn as_u32(self) -> u32 {
193 self as u32
194 }
195}
196
197#[repr(u32)]
200#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord)]
201pub enum Level {
202 Trace = TOCAT_TRACE,
203 Debug = TOCAT_DEBUG,
204 #[default]
207 Info = TOCAT_INFO,
208 Warn = TOCAT_WARN,
209 Error = TOCAT_ERROR,
210}
211
212impl Level {
213 pub const fn from_u32(value: u32) -> Self {
214 match value {
215 TOCAT_TRACE => Self::Trace,
216 TOCAT_DEBUG => Self::Debug,
217 TOCAT_WARN => Self::Warn,
218 TOCAT_ERROR => Self::Error,
219 _ => Self::Info,
220 }
221 }
222
223 pub const fn as_u32(self) -> u32 {
224 self as u32
225 }
226}
227
228pub mod exports {
231 pub const MEMORY: &str = "memory";
232 pub const ABI_VERSION: &str = "tocat_abi_version";
233 pub const OUTBOX: &str = "tocat_outbox";
234 pub const ALLOC: &str = "tocat_alloc";
235 pub const INIT: &str = "tocat_init";
236 pub const ON_BYTES: &str = "tocat_on_bytes";
237 pub const ON_EOF: &str = "tocat_on_eof";
238 pub const ON_TICK: &str = "tocat_on_tick";
239 pub const TICK_INTERVAL_NS: &str = "tocat_tick_interval_ns";
240 pub const DATAGRAM_SAFE: &str = "tocat_datagram_safe";
241}
242
243#[cfg(test)]
244mod tests {
245 use super::*;
246
247 #[test]
248 fn the_enums_are_the_constants() {
249 assert_eq!(Emit::Buffered.as_u32(), TOCAT_EMIT_BUFFERED);
250 assert_eq!(Level::Warn.as_u32(), TOCAT_WARN);
251 assert_eq!(
252 Emit::from_u32(TOCAT_EMIT_PASSTHROUGH),
253 Some(Emit::Passthrough)
254 );
255 assert_eq!(Emit::from_u32(3), None);
256 assert_eq!(Level::from_u32(99), Level::Info);
257 }
258
259 #[test]
260 fn an_outbox_starts_and_resets_empty() {
261 let mut outbox = Outbox::new();
262 assert_eq!(outbox.emit(), Some(Emit::Pending));
263
264 outbox.set(TOCAT_FLAG_HALT);
265 outbox.set_emit(Emit::Buffered);
266 assert!(outbox.has(TOCAT_FLAG_HALT));
267
268 outbox.reset();
269 assert_eq!(outbox, Outbox::new());
270 assert!(!outbox.has(TOCAT_FLAG_HALT));
271 }
272}