use crate::ArrowBatchMetadata;
pub const KEY_TIMESTAMP_SDK_IPC_ENCODE: &str = "rerun:timestamp_sdk_ipc_encoded";
pub const KEY_TIMESTAMP_VIEWER_IPC_DECODED: &str = "rerun:timestamp_viewer_ipc_decoded";
pub fn now_timestamp() -> String {
encode_timestamp(web_time::SystemTime::now())
}
pub fn encode_timestamp(timestamp: web_time::SystemTime) -> String {
let duration = timestamp
.duration_since(web_time::UNIX_EPOCH)
.unwrap_or_default();
format!("{}.{:09}", duration.as_secs(), duration.subsec_nanos())
}
pub fn parse_timestamp(timestamp: &str) -> Option<web_time::SystemTime> {
let parts: Vec<&str> = timestamp.split('.').collect();
if parts.len() != 2 {
return None;
}
let seconds = parts[0].parse::<u64>().ok()?;
let nanos = parts[1].parse::<u32>().ok()?;
Some(web_time::UNIX_EPOCH + web_time::Duration::new(seconds, nanos))
}
#[test]
fn test_timestamp_encoding() {
let now = web_time::SystemTime::now();
let encoded = encode_timestamp(now);
assert_eq!(encoded.len(), 20); let parsed = parse_timestamp(&encoded).unwrap();
assert_eq!(parsed, now);
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TimestampMetadata {
pub grpc_encoded_at: Option<web_time::SystemTime>,
pub grpc_decoded_at: Option<web_time::SystemTime>,
}
impl TimestampMetadata {
pub fn parse_record_batch_metadata(metadata: &ArrowBatchMetadata) -> Self {
let grpc_encoded_at = metadata
.get(KEY_TIMESTAMP_SDK_IPC_ENCODE)
.and_then(|s| parse_timestamp(s.as_str()));
let grpc_decoded_at = metadata
.get(KEY_TIMESTAMP_VIEWER_IPC_DECODED)
.and_then(|s| parse_timestamp(s.as_str()));
if cfg!(debug_assertions) {
if grpc_encoded_at.is_some() && grpc_decoded_at.is_none() {
if false {
re_log::warn_once!(
"Received a batch with an encode timestamp but no decode timestamp. Latency measurements will be incomplete."
);
}
}
if grpc_decoded_at.is_some() && grpc_encoded_at.is_none() {
re_log::warn_once!(
"Received a batch with a decode timestamp but no encode timestamp. Latency measurements will be incomplete."
);
}
}
Self {
grpc_encoded_at,
grpc_decoded_at,
}
}
pub fn to_metadata(&self) -> impl Iterator<Item = (String, String)> {
let Self {
grpc_encoded_at,
grpc_decoded_at,
} = self;
let mut metadata = Vec::new();
if let Some(last_encoded_at) = grpc_encoded_at {
metadata.push((
KEY_TIMESTAMP_SDK_IPC_ENCODE.to_owned(),
encode_timestamp(*last_encoded_at),
));
}
if let Some(last_decoded_at) = grpc_decoded_at {
metadata.push((
KEY_TIMESTAMP_VIEWER_IPC_DECODED.to_owned(),
encode_timestamp(*last_decoded_at),
));
}
metadata.into_iter()
}
}