use crate::event_id::EventId;
use crate::stringtable::StringId;
#[cfg(target_endian = "big")]
use std::convert::TryInto;
#[derive(Eq, PartialEq, Debug)]
#[repr(C)]
pub struct RawEvent {
pub event_kind: StringId,
pub event_id: EventId,
pub thread_id: u32,
pub start_time_lower: u32,
pub end_time_lower: u32,
pub start_and_end_upper: u32,
}
const INSTANT_TIMESTAMP_MARKER: u64 = 0xFFFF_FFFF_FFFF;
pub const MAX_INSTANT_TIMESTAMP: u64 = 0xFFFF_FFFF_FFFF;
pub const MAX_INTERVAL_TIMESTAMP: u64 = INSTANT_TIMESTAMP_MARKER - 1;
impl RawEvent {
#[inline]
pub fn new_interval(
event_kind: StringId,
event_id: EventId,
thread_id: u32,
start_nanos: u64,
end_nanos: u64,
) -> RawEvent {
assert!(start_nanos <= end_nanos);
assert!(end_nanos <= MAX_INTERVAL_TIMESTAMP);
let start_time_lower = start_nanos as u32;
let end_time_lower = end_nanos as u32;
let start_time_upper = (start_nanos >> 16) as u32 & 0xFFFF_0000;
let end_time_upper = (end_nanos >> 32) as u32;
let start_and_end_upper = start_time_upper | end_time_upper;
RawEvent {
event_kind,
event_id,
thread_id,
start_time_lower,
end_time_lower,
start_and_end_upper,
}
}
#[inline]
pub fn new_instant(
event_kind: StringId,
event_id: EventId,
thread_id: u32,
timestamp_ns: u64,
) -> RawEvent {
assert!(timestamp_ns <= MAX_INSTANT_TIMESTAMP);
let start_time_lower = timestamp_ns as u32;
let end_time_lower = 0xFFFF_FFFF;
let start_time_upper = (timestamp_ns >> 16) as u32;
let start_and_end_upper = start_time_upper | 0x0000_FFFF;
RawEvent {
event_kind,
event_id,
thread_id,
start_time_lower,
end_time_lower,
start_and_end_upper,
}
}
#[inline]
pub fn start_nanos(&self) -> u64 {
self.start_time_lower as u64 | (((self.start_and_end_upper & 0xFFFF_0000) as u64) << 16)
}
#[inline]
pub fn end_nanos(&self) -> u64 {
self.end_time_lower as u64 | (((self.start_and_end_upper & 0x0000_FFFF) as u64) << 32)
}
#[inline]
pub fn is_instant(&self) -> bool {
self.end_nanos() == INSTANT_TIMESTAMP_MARKER
}
#[inline]
pub fn serialize(&self, bytes: &mut [u8]) {
assert!(bytes.len() == std::mem::size_of::<RawEvent>());
#[cfg(target_endian = "little")]
{
let raw_event_bytes: &[u8] = unsafe {
std::slice::from_raw_parts(
self as *const _ as *const u8,
std::mem::size_of::<RawEvent>(),
)
};
bytes.copy_from_slice(raw_event_bytes);
}
#[cfg(target_endian = "big")]
{
bytes[0..4].copy_from_slice(&self.event_kind.as_u32().to_le_bytes());
bytes[4..8].copy_from_slice(&self.event_id.as_u32().to_le_bytes());
bytes[8..12].copy_from_slice(&self.thread_id.to_le_bytes());
bytes[12..16].copy_from_slice(&self.start_time_lower.to_le_bytes());
bytes[16..20].copy_from_slice(&self.end_time_lower.to_le_bytes());
bytes[20..24].copy_from_slice(&self.start_and_end_upper.to_le_bytes());
}
}
#[inline]
pub fn deserialize(bytes: &[u8]) -> RawEvent {
assert!(bytes.len() == std::mem::size_of::<RawEvent>());
#[cfg(target_endian = "little")]
{
let mut raw_event = RawEvent::default();
unsafe {
let raw_event = std::slice::from_raw_parts_mut(
&mut raw_event as *mut RawEvent as *mut u8,
std::mem::size_of::<RawEvent>(),
);
raw_event.copy_from_slice(bytes);
};
raw_event
}
#[cfg(target_endian = "big")]
{
RawEvent {
event_kind: StringId::new(u32::from_le_bytes(bytes[0..4].try_into().unwrap())),
event_id: EventId::from_u32(u32::from_le_bytes(bytes[4..8].try_into().unwrap())),
thread_id: u32::from_le_bytes(bytes[8..12].try_into().unwrap()),
start_time_lower: u32::from_le_bytes(bytes[12..16].try_into().unwrap()),
end_time_lower: u32::from_le_bytes(bytes[16..20].try_into().unwrap()),
start_and_end_upper: u32::from_le_bytes(bytes[20..24].try_into().unwrap()),
}
}
}
}
impl Default for RawEvent {
fn default() -> Self {
RawEvent {
event_kind: StringId::INVALID,
event_id: EventId::INVALID,
thread_id: 0,
start_time_lower: 0,
end_time_lower: 0,
start_and_end_upper: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn raw_event_has_expected_size() {
assert_eq!(std::mem::size_of::<RawEvent>(), 24);
}
#[test]
fn is_instant() {
assert!(RawEvent::new_instant(StringId::INVALID, EventId::INVALID, 987, 0,).is_instant());
assert!(RawEvent::new_instant(
StringId::INVALID,
EventId::INVALID,
987,
MAX_INSTANT_TIMESTAMP,
)
.is_instant());
assert!(!RawEvent::new_interval(
StringId::INVALID,
EventId::INVALID,
987,
0,
MAX_INTERVAL_TIMESTAMP,
)
.is_instant());
}
#[test]
#[should_panic]
fn invalid_instant_timestamp() {
let _ = RawEvent::new_instant(
StringId::INVALID,
EventId::INVALID,
123,
MAX_INSTANT_TIMESTAMP + 1,
);
}
#[test]
#[should_panic]
fn invalid_start_timestamp() {
let _ = RawEvent::new_interval(
StringId::INVALID,
EventId::INVALID,
123,
MAX_INTERVAL_TIMESTAMP + 1,
MAX_INTERVAL_TIMESTAMP + 1,
);
}
#[test]
#[should_panic]
fn invalid_end_timestamp() {
let _ = RawEvent::new_interval(
StringId::INVALID,
EventId::INVALID,
123,
0,
MAX_INTERVAL_TIMESTAMP + 3,
);
}
#[test]
#[should_panic]
fn invalid_end_timestamp2() {
let _ = RawEvent::new_interval(
StringId::INVALID,
EventId::INVALID,
123,
0,
INSTANT_TIMESTAMP_MARKER,
);
}
#[test]
#[should_panic]
fn start_greater_than_end_timestamp() {
let _ = RawEvent::new_interval(
StringId::INVALID,
EventId::INVALID,
123,
1,
0,
);
}
#[test]
fn start_equal_to_end_timestamp() {
let _ = RawEvent::new_interval(StringId::INVALID, EventId::INVALID, 123, 1, 1);
}
#[test]
fn interval_timestamp_decoding() {
let e = RawEvent::new_interval(
StringId::INVALID,
EventId::INVALID,
1234,
MAX_INTERVAL_TIMESTAMP,
MAX_INTERVAL_TIMESTAMP,
);
assert_eq!(e.start_nanos(), MAX_INTERVAL_TIMESTAMP);
assert_eq!(e.end_nanos(), MAX_INTERVAL_TIMESTAMP);
let e = RawEvent::new_interval(StringId::INVALID, EventId::INVALID, 1234, 0, 0);
assert_eq!(e.start_nanos(), 0);
assert_eq!(e.end_nanos(), 0);
let e = RawEvent::new_interval(
StringId::INVALID,
EventId::INVALID,
1234,
0,
MAX_INTERVAL_TIMESTAMP,
);
assert_eq!(e.start_nanos(), 0);
assert_eq!(e.end_nanos(), MAX_INTERVAL_TIMESTAMP);
let e = RawEvent::new_interval(
StringId::INVALID,
EventId::INVALID,
1234,
0x1234567890,
0x1234567890A,
);
assert_eq!(e.start_nanos(), 0x1234567890);
assert_eq!(e.end_nanos(), 0x1234567890A);
}
#[test]
fn instant_timestamp_decoding() {
assert_eq!(
RawEvent::new_instant(StringId::INVALID, EventId::INVALID, 987, 0,).start_nanos(),
0
);
assert_eq!(
RawEvent::new_instant(
StringId::INVALID,
EventId::INVALID,
987,
MAX_INSTANT_TIMESTAMP,
)
.start_nanos(),
MAX_INSTANT_TIMESTAMP
);
}
}