1#[derive(Debug, Clone, Copy, PartialEq, Eq)]
8pub struct DebugRecord {
9 pub fmt_id: u32,
12 pub args: [u32; 3],
14}
15
16#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
18#[non_exhaustive]
19pub enum ProtocolError {
20 #[error("{buffer} byte length overflow. Fix: {fix}")]
22 ByteLengthOverflow {
23 buffer: &'static str,
25 fix: &'static str,
27 },
28 #[error("{buffer} has {byte_len} bytes, not a whole number of u32 words. Fix: {fix}")]
30 MisalignedByteLength {
31 buffer: &'static str,
33 byte_len: usize,
35 fix: &'static str,
37 },
38 #[error("{buffer} is missing word {word_idx} in {byte_len} bytes. Fix: {fix}")]
40 MissingWord {
41 buffer: &'static str,
43 word_idx: usize,
45 byte_len: usize,
47 fix: &'static str,
49 },
50}
51
52pub const SLOT_WORDS: u32 = 16;
56
57pub const STATUS_WORD: u32 = 0;
59
60pub const OPCODE_WORD: u32 = 1;
62
63pub const TENANT_WORD: u32 = 2;
65
66pub const PRIORITY_WORD: u32 = 3;
68
69pub const ARG0_WORD: u32 = 4;
72
73pub const ARGS_PER_SLOT: u32 = SLOT_WORDS - ARG0_WORD;
75
76pub mod control;
78pub mod debug;
80pub mod opcode;
82pub mod slot;
84
85pub const CONTROL_MIN_WORDS: u32 = 160;
90pub const MAX_OBSERVABLE_SLOTS: u32 = u32::MAX - control::OBSERVABLE_BASE;
93pub const MAX_ENCODED_OBSERVABLE_SLOTS: u32 = 1_048_576;
95pub const MAX_RING_SLOTS: u32 = u32::MAX / SLOT_WORDS;
97pub const MAX_ENCODED_RING_SLOTS: u32 = 1_048_576;
99pub const MAX_DEBUG_RECORDS: u32 = u32::MAX / debug::RECORD_WORDS;
101pub const MAX_ENCODED_DEBUG_RECORDS: u32 = 1_048_576;
103
104mod codec;
105
106pub use codec::{
107 control_byte_len, count_done_ring_slots, debug_log_byte_len, encode_control,
108 encode_empty_debug_log, encode_empty_ring, read_debug_log, read_debug_log_into,
109 read_done_count, read_epoch, read_metrics, read_metrics_into, read_observable, ring_byte_len,
110 try_count_done_ring_slots, try_encode_control, try_encode_control_into,
111 try_encode_empty_debug_log, try_encode_empty_debug_log_into, try_encode_empty_ring,
112 try_encode_empty_ring_into, try_read_debug_log, try_read_debug_log_into, try_read_done_count,
113 try_read_epoch, try_read_metrics, try_read_metrics_into, try_read_observable,
114};
115pub(crate) use codec::read_word;
116
117#[must_use]
127pub fn encode_load_miss(resource_id: u32, prefetch: bool) -> Vec<u8> {
128 try_encode_load_miss(resource_id, prefetch).unwrap_or_default()
129}
130
131pub fn try_encode_load_miss(resource_id: u32, prefetch: bool) -> Result<Vec<u8>, ProtocolError> {
138 let total_bytes = try_slot_byte_len()?;
139 let mut bytes = Vec::new();
140 codec::try_reserve_protocol_capacity(
141 &mut bytes,
142 total_bytes,
143 "slot",
144 "load-miss slot encode could not reserve host staging bytes; reuse a preallocated slot buffer",
145 )?;
146 try_encode_load_miss_into(resource_id, prefetch, &mut bytes)?;
147 Ok(bytes)
148}
149
150pub fn try_encode_load_miss_into(
159 resource_id: u32,
160 prefetch: bool,
161 dst: &mut Vec<u8>,
162) -> Result<(), ProtocolError> {
163 let total_bytes = try_slot_byte_len()?;
164 dst.clear();
165 codec::try_reserve_protocol_capacity(
166 dst,
167 total_bytes,
168 "slot",
169 "load-miss slot encode could not reserve caller-owned staging bytes; reuse a larger slot buffer",
170 )?;
171 dst.resize(total_bytes, 0);
172
173 codec::write_word(dst, try_slot_word_index(0, STATUS_WORD)?, slot::PUBLISHED);
174 codec::write_word(dst, try_slot_word_index(0, OPCODE_WORD)?, opcode::LOAD_MISS);
175 codec::write_word(dst, try_slot_word_index(0, TENANT_WORD)?, 0);
176 codec::write_word(dst, try_slot_word_index(0, PRIORITY_WORD)?, 0);
177 codec::write_word(dst, try_slot_word_index(0, ARG0_WORD)?, resource_id);
178 let prefetch_word = ARG0_WORD
179 .checked_add(1)
180 .ok_or(ProtocolError::ByteLengthOverflow {
181 buffer: "slot",
182 fix: "load-miss argument word overflows u32; keep ARG0_WORD within SLOT_WORDS",
183 })?;
184 codec::write_word(
185 dst,
186 try_slot_word_index(0, prefetch_word)?,
187 u32::from(prefetch),
188 );
189 Ok(())
190}
191
192#[must_use]
198pub fn decode_load_miss(ring_bytes: &[u8], slot: u32) -> Option<(u32, bool)> {
199 let opcode_word = codec::read_word(ring_bytes, try_slot_word_index(slot, OPCODE_WORD).ok()?)?;
200 if opcode_word != opcode::LOAD_MISS {
201 return None;
202 }
203 let resource_id = codec::read_word(ring_bytes, try_slot_word_index(slot, ARG0_WORD).ok()?)?;
204 let prefetch_word = ARG0_WORD.checked_add(1)?;
205 let prefetch =
206 codec::read_word(ring_bytes, try_slot_word_index(slot, prefetch_word).ok()?)? != 0;
207 Some((resource_id, prefetch))
208}
209
210pub fn try_slot_byte_len() -> Result<usize, ProtocolError> {
216 codec::words_to_bytes(SLOT_WORDS).ok_or(ProtocolError::ByteLengthOverflow {
217 buffer: "slot",
218 fix: "slot byte length overflows host address space; keep SLOT_WORDS within the host index ABI",
219 })
220}
221
222pub fn try_word_index(word: u32) -> Result<usize, ProtocolError> {
228 usize::try_from(word).map_err(|_| ProtocolError::ByteLengthOverflow {
229 buffer: "slot",
230 fix: "protocol word index cannot fit host usize; keep protocol word constants within the host index ABI",
231 })
232}
233
234pub fn try_slot_word_base(slot: u32) -> Result<usize, ProtocolError> {
241 let base_words = slot
242 .checked_mul(SLOT_WORDS)
243 .ok_or(ProtocolError::ByteLengthOverflow {
244 buffer: "ring",
245 fix: "slot word base overflows the u32 protocol word ABI; shard the megakernel ring before host access",
246 })?;
247 try_word_index(base_words)
248}
249
250pub fn try_slot_word_index(slot: u32, word: u32) -> Result<usize, ProtocolError> {
257 if word >= SLOT_WORDS {
258 return Err(ProtocolError::ByteLengthOverflow {
259 buffer: "slot",
260 fix: "slot-local word is outside SLOT_WORDS; keep protocol constants within the slot layout",
261 });
262 }
263 try_slot_word_base(slot)?
264 .checked_add(try_word_index(word)?)
265 .ok_or(ProtocolError::ByteLengthOverflow {
266 buffer: "ring",
267 fix: "slot word index overflows host address space; shard the megakernel ring before host access",
268 })
269}
270
271#[deprecated(since = "0.5.0", note = "use `encode_load_miss`")]
276#[must_use]
277pub fn encode_expert_miss(resource_id: u32, prefetch: bool) -> Vec<u8> {
278 encode_load_miss(resource_id, prefetch)
279}
280
281#[deprecated(since = "0.5.0", note = "use `decode_load_miss`")]
283#[must_use]
284pub fn decode_expert_miss(ring_bytes: &[u8], slot: u32) -> Option<(u32, bool)> {
285 decode_load_miss(ring_bytes, slot)
286}
287
288#[cfg(test)]
289mod tests;