use std::mem;
use std::ptr;
use skippy_ffi::{SkippyRuntimeEventV1 as RawRuntimeEvent, Status};
use super::RUNTIME_EVENT_V1_ABI_VERSION;
use super::wire_types::{BaseRawRuntimeEvent, MAX_DETAIL_BYTES, RuntimeEvent};
pub const INLINE_DETAIL_BYTES: usize = 256;
const SUMMARY_PRESENT_ALL: u8 = 0b1111;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
pub struct NativeEventRecord {
pub abi_version: u32,
pub struct_size: u32,
pub category: u32,
pub kind: u32,
pub emitter: u32,
pub reserved0: u32,
pub sequence: u64,
pub timestamp_mono_ns: u64,
pub model_id: u64,
pub stage_id: u64,
pub session_id: u64,
pub progress_current: u64,
pub progress_total: u64,
pub progress_unit: u32,
pub failure_code: u32,
pub status: Status,
pub reserved1: u32,
pub numeric_summary: [u64; 4],
pub numeric_summary_present: u8,
pub detail_truncated: bool,
pub detail_len: u16,
pub detail: [u8; INLINE_DETAIL_BYTES],
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RecordRejection {
Null,
ShortStruct,
AbiVersion(u32),
OversizedDetail,
}
impl NativeEventRecord {
pub unsafe fn from_raw_ptr(event: *const RawRuntimeEvent) -> Result<Self, RecordRejection> {
if event.is_null() {
return Err(RecordRejection::Null);
}
let struct_size = unsafe { ptr::read_unaligned(ptr::addr_of!((*event).struct_size)) };
if (struct_size as usize) < mem::size_of::<BaseRawRuntimeEvent>() {
return Err(RecordRejection::ShortStruct);
}
let covers_extension = (struct_size as usize) >= mem::size_of::<RawRuntimeEvent>();
let base = unsafe { &*event.cast::<BaseRawRuntimeEvent>() };
if base.abi_version != RUNTIME_EVENT_V1_ABI_VERSION {
return Err(RecordRejection::AbiVersion(base.abi_version));
}
let declared = usize::try_from(base.detail_len)
.ok()
.filter(|declared| *declared <= MAX_DETAIL_BYTES)
.ok_or(RecordRejection::OversizedDetail)?;
let mut detail = [0u8; INLINE_DETAIL_BYTES];
let copied = if declared == 0 || base.detail_ptr.is_null() {
0
} else {
let copied = declared.min(INLINE_DETAIL_BYTES);
let source =
unsafe { std::slice::from_raw_parts(base.detail_ptr.cast::<u8>(), copied) };
detail[..copied].copy_from_slice(source);
copied
};
let (numeric_summary, numeric_summary_present) = if covers_extension {
let full = unsafe { &*event };
(
[
full.numeric_summary_0,
full.numeric_summary_1,
full.numeric_summary_2,
full.numeric_summary_3,
],
SUMMARY_PRESENT_ALL,
)
} else {
([0; 4], 0)
};
Ok(Self {
abi_version: base.abi_version,
struct_size,
category: base.category.0,
kind: base.kind.0,
emitter: base.emitter.0,
reserved0: base.reserved0,
sequence: base.sequence,
timestamp_mono_ns: base.timestamp_mono_ns,
model_id: base.model_id,
stage_id: base.stage_id,
session_id: base.session_id,
progress_current: base.progress_current,
progress_total: base.progress_total,
progress_unit: base.progress_unit.0,
failure_code: base.failure_code.0,
status: base.status,
reserved1: base.reserved1,
numeric_summary,
numeric_summary_present,
detail_truncated: declared > copied,
detail_len: u16::try_from(copied).unwrap_or(u16::MAX),
detail,
})
}
#[must_use]
pub fn detail(&self) -> &[u8] {
&self.detail[..usize::from(self.detail_len)]
}
fn summary(&self, index: usize) -> Option<u64> {
(self.numeric_summary_present & (1 << index) != 0).then(|| self.numeric_summary[index])
}
#[must_use]
pub fn to_event(&self) -> RuntimeEvent {
RuntimeEvent {
abi_version: self.abi_version,
struct_size: self.struct_size,
category: skippy_ffi::SkippyRuntimeEventCategory(self.category).into(),
kind: skippy_ffi::SkippyRuntimeEventKind(self.kind).into(),
emitter: skippy_ffi::SkippyRuntimeEventEmitterKind(self.emitter).into(),
reserved0: self.reserved0,
sequence: self.sequence,
timestamp_mono_ns: self.timestamp_mono_ns,
model_id: self.model_id,
stage_id: self.stage_id,
session_id: self.session_id,
progress_current: self.progress_current,
progress_total: self.progress_total,
progress_unit: skippy_ffi::SkippyRuntimeEventProgressUnit(self.progress_unit).into(),
failure_code: skippy_ffi::SkippyRuntimeEventFailureCode(self.failure_code).into(),
status: self.status,
reserved1: self.reserved1,
detail_bytes: self.detail().to_vec(),
numeric_summary_0: self.summary(0),
numeric_summary_1: self.summary(1),
numeric_summary_2: self.summary(2),
numeric_summary_3: self.summary(3),
}
}
}