#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct DebugRecord {
pub fmt_id: u32,
pub args: [u32; 3],
}
#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
#[non_exhaustive]
pub enum ProtocolError {
#[error("{buffer} byte length overflow. Fix: {fix}")]
ByteLengthOverflow {
buffer: &'static str,
fix: &'static str,
},
#[error("{buffer} has {byte_len} bytes, not a whole number of u32 words. Fix: {fix}")]
MisalignedByteLength {
buffer: &'static str,
byte_len: usize,
fix: &'static str,
},
#[error("{buffer} is missing word {word_idx} in {byte_len} bytes. Fix: {fix}")]
MissingWord {
buffer: &'static str,
word_idx: usize,
byte_len: usize,
fix: &'static str,
},
}
pub const SLOT_WORDS: u32 = 16;
pub const STATUS_WORD: u32 = 0;
pub const OPCODE_WORD: u32 = 1;
pub const TENANT_WORD: u32 = 2;
pub const PRIORITY_WORD: u32 = 3;
pub const ARG0_WORD: u32 = 4;
pub const ARGS_PER_SLOT: u32 = SLOT_WORDS - ARG0_WORD;
pub mod control;
pub mod debug;
pub mod opcode;
pub mod slot;
pub const CONTROL_MIN_WORDS: u32 = 160;
pub const MAX_OBSERVABLE_SLOTS: u32 = u32::MAX - control::OBSERVABLE_BASE;
pub const MAX_ENCODED_OBSERVABLE_SLOTS: u32 = 1_048_576;
pub const MAX_RING_SLOTS: u32 = u32::MAX / SLOT_WORDS;
pub const MAX_ENCODED_RING_SLOTS: u32 = 1_048_576;
pub const MAX_DEBUG_RECORDS: u32 = u32::MAX / debug::RECORD_WORDS;
pub const MAX_ENCODED_DEBUG_RECORDS: u32 = 1_048_576;
mod codec;
pub use codec::{
control_byte_len, count_done_ring_slots, debug_log_byte_len, encode_control,
encode_empty_debug_log, encode_empty_ring, read_debug_log, read_debug_log_into,
read_done_count, read_epoch, read_metrics, read_metrics_into, read_observable, ring_byte_len,
try_count_done_ring_slots, try_encode_control, try_encode_control_into,
try_encode_empty_debug_log, try_encode_empty_debug_log_into, try_encode_empty_ring,
try_encode_empty_ring_into, try_read_debug_log, try_read_debug_log_into, try_read_done_count,
try_read_epoch, try_read_metrics, try_read_metrics_into, try_read_observable,
};
#[must_use]
pub fn encode_load_miss(resource_id: u32, prefetch: bool) -> Vec<u8> {
let mut bytes = vec![0u8; slot_byte_len_or_panic()];
codec::write_word(
&mut bytes,
word_index_or_panic(STATUS_WORD),
slot::PUBLISHED,
);
codec::write_word(
&mut bytes,
word_index_or_panic(OPCODE_WORD),
opcode::LOAD_MISS,
);
codec::write_word(&mut bytes, word_index_or_panic(TENANT_WORD), 0);
codec::write_word(&mut bytes, word_index_or_panic(PRIORITY_WORD), 0);
codec::write_word(&mut bytes, word_index_or_panic(ARG0_WORD), resource_id);
codec::write_word(
&mut bytes,
word_index_or_panic(ARG0_WORD.checked_add(1).unwrap_or_else(|| {
panic!("megakernel load-miss arg word overflowed u32. Fix: keep ARG0_WORD within SLOT_WORDS.")
})),
u32::from(prefetch),
);
bytes
}
#[must_use]
pub fn decode_load_miss(ring_bytes: &[u8], slot: u32) -> Option<(u32, bool)> {
let slot_base = slot_word_base(slot)?;
let opcode_word = codec::read_word(ring_bytes, checked_slot_word(slot_base, OPCODE_WORD)?)?;
if opcode_word != opcode::LOAD_MISS {
return None;
}
let resource_id = codec::read_word(ring_bytes, checked_slot_word(slot_base, ARG0_WORD)?)?;
let prefetch = codec::read_word(
ring_bytes,
checked_slot_word(slot_base, ARG0_WORD.checked_add(1)?)?,
)? != 0;
Some((resource_id, prefetch))
}
fn slot_byte_len_or_panic() -> usize {
usize::try_from(SLOT_WORDS)
.unwrap_or_else(|error| {
panic!("SLOT_WORDS cannot fit usize: {error}. Fix: keep SLOT_WORDS within the host index ABI.")
})
.checked_mul(4)
.unwrap_or_else(|| {
panic!("megakernel slot byte length overflowed usize. Fix: keep SLOT_WORDS within the host index ABI.")
})
}
fn word_index_or_panic(word: u32) -> usize {
usize::try_from(word).unwrap_or_else(|error| {
panic!("megakernel protocol word index cannot fit usize: {error}. Fix: keep protocol word constants within the host index ABI.")
})
}
fn slot_word_base(slot: u32) -> Option<usize> {
let base_words = slot.checked_mul(SLOT_WORDS)?;
usize::try_from(base_words).ok()
}
fn checked_slot_word(slot_base: usize, word: u32) -> Option<usize> {
slot_base.checked_add(usize::try_from(word).ok()?)
}
#[deprecated(since = "0.5.0", note = "use `encode_load_miss`")]
#[must_use]
pub fn encode_expert_miss(resource_id: u32, prefetch: bool) -> Vec<u8> {
encode_load_miss(resource_id, prefetch)
}
#[deprecated(since = "0.5.0", note = "use `decode_load_miss`")]
#[must_use]
pub fn decode_expert_miss(ring_bytes: &[u8], slot: u32) -> Option<(u32, bool)> {
decode_load_miss(ring_bytes, slot)
}
#[cfg(test)]
mod tests;