use super::summary::Summary;
use crc32fast::Hasher;
use std::fs::File;
use std::io::{BufWriter, Write};
use std::path::Path;
use std::time::{SystemTime, UNIX_EPOCH};
pub struct EventWriter {
writer: BufWriter<File>,
event_count: u64,
}
impl EventWriter {
pub fn new(path: impl AsRef<Path>) -> std::io::Result<Self> {
let file = File::create(path)?;
let mut writer = BufWriter::new(file);
let header = b""; writer.write_all(header)?;
Ok(Self {
writer,
event_count: 0,
})
}
pub fn write_summary(&mut self, summary: Summary) {
let event = Event {
wall_time: get_wall_time(),
step: summary.step as i64,
summary: Some(summary),
};
self.write_event(event);
}
fn write_event(&mut self, event: Event) {
let event_bytes = serialize_event(&event);
self.write_record(&event_bytes);
self.event_count += 1;
}
fn write_record(&mut self, data: &[u8]) {
let length = data.len() as u64;
self.writer.write_all(&length.to_le_bytes()).unwrap();
let length_crc = masked_crc32(&length.to_le_bytes());
self.writer.write_all(&length_crc.to_le_bytes()).unwrap();
self.writer.write_all(data).unwrap();
let data_crc = masked_crc32(data);
self.writer.write_all(&data_crc.to_le_bytes()).unwrap();
}
pub fn flush(&mut self) {
self.writer.flush().unwrap();
}
}
#[derive(Debug, Clone)]
struct Event {
wall_time: f64,
step: i64,
summary: Option<Summary>,
}
fn get_wall_time() -> f64 {
SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_secs_f64()
}
fn masked_crc32(data: &[u8]) -> u32 {
let mut hasher = Hasher::new();
hasher.update(data);
let crc = hasher.finalize();
((crc >> 15) | (crc << 17)).wrapping_add(0xa282ead8)
}
fn serialize_event(event: &Event) -> Vec<u8> {
let json = serde_json::json!({
"wall_time": event.wall_time,
"step": event.step,
"summary": event.summary.as_ref().map(|s| {
serde_json::json!({
"tag": s.tag,
"step": s.step,
"value": match &s.value {
super::summary::SummaryValue::Scalar(v) => {
serde_json::json!({ "scalar": v })
},
super::summary::SummaryValue::Histogram(h) => {
serde_json::json!({ "histogram": h })
},
super::summary::SummaryValue::Image(img) => {
serde_json::json!({
"image": {
"height": img.height,
"width": img.width,
"colorspace": img.channels,
}
})
},
super::summary::SummaryValue::Text(t) => {
serde_json::json!({ "text": t })
},
_ => serde_json::json!(null),
}
})
})
});
json.to_string().into_bytes()
}
impl Drop for EventWriter {
fn drop(&mut self) {
self.flush();
}
}