use crc32c::crc32c;
use std::io::Write;
use crate::error::WsiError;
fn masked_crc32c(bytes: &[u8]) -> u32 {
let crc = crc32c(bytes);
crc.rotate_right(15).wrapping_add(0xa282ead8)
}
pub struct TfRecordWriter<W: Write> {
writer: W,
}
impl<W: Write> TfRecordWriter<W> {
pub fn new(writer: W) -> Self {
Self { writer }
}
pub fn write_record(&mut self, data: &[u8]) -> Result<(), WsiError> {
let length = (data.len() as u64).to_le_bytes();
let masked_crc_length = masked_crc32c(&length).to_le_bytes();
let masked_crc_data = masked_crc32c(data).to_le_bytes();
self.writer.write_all(&length)?;
self.writer.write_all(&masked_crc_length)?;
self.writer.write_all(data)?;
self.writer.write_all(&masked_crc_data)?;
Ok(())
}
}
fn write_varint(buf: &mut Vec<u8>, mut value: u64) {
loop {
let byte = (value & 0x7f) as u8;
value >>= 7;
if value == 0 {
buf.push(byte);
break;
}
buf.push(byte | 0x80);
}
}
fn write_length_delimited(buf: &mut Vec<u8>, field: u32, payload: &[u8]) {
write_varint(buf, ((field as u64) << 3) | 2); write_varint(buf, payload.len() as u64);
buf.extend_from_slice(payload);
}
fn encode_bytes_list(value: &[u8]) -> Vec<u8> {
let mut buf = Vec::new();
write_length_delimited(&mut buf, 1, value);
buf
}
fn encode_int64_list(values: &[i64]) -> Vec<u8> {
let mut packed = Vec::new();
for &v in values {
write_varint(&mut packed, v as u64); }
let mut buf = Vec::new();
write_length_delimited(&mut buf, 1, &packed); buf
}
pub(crate) enum FeatureValue {
Bytes(Vec<u8>),
Int64s(Vec<i64>),
}
fn encode_feature(value: &FeatureValue) -> Vec<u8> {
let mut buf = Vec::new();
match value {
FeatureValue::Bytes(b) => {
let encoded = encode_bytes_list(b);
write_length_delimited(&mut buf, 1, &encoded) }
FeatureValue::Int64s(i) => {
let encoded = encode_int64_list(i);
write_length_delimited(&mut buf, 3, &encoded); }
};
buf
}
fn encode_feature_map_entry(key: &str, value: &FeatureValue) -> Vec<u8> {
let mut entry = Vec::new();
write_length_delimited(&mut entry, 1, key.as_bytes()); write_length_delimited(&mut entry, 2, &encode_feature(value));
let mut buf = Vec::new();
write_length_delimited(&mut buf, 1, &entry);
buf
}
pub(crate) fn encode_example(fields: &[(&str, FeatureValue)]) -> Vec<u8> {
let mut features = Vec::new();
for (key, value) in fields {
features.extend_from_slice(&encode_feature_map_entry(key, value));
}
let mut example = Vec::new();
write_length_delimited(&mut example, 1, &features); example
}
pub(crate) fn tile_record(slide_name: &str, bytes: Vec<u8>, loc_x: i64, loc_y: i64) -> Vec<u8> {
encode_example(&[
("slide", FeatureValue::Bytes(slide_name.as_bytes().to_vec())),
("image_raw", FeatureValue::Bytes(bytes)),
("loc_x", FeatureValue::Int64s(vec![loc_x])),
("loc_y", FeatureValue::Int64s(vec![loc_y])),
])
}
#[cfg(test)]
mod tests {
use super::*;
fn unmask(masked: u32) -> u32 {
let rot = masked.wrapping_sub(0xa282ead8);
rot.rotate_left(15)
}
#[test]
fn crc32c_matches_standard_check_value() {
assert_eq!(crc32c(b"123456789"), 0xE3069283);
}
#[test]
fn masked_crc32c_round_trips_through_unmask() {
let inputs: &[&[u8]] = &[
b"",
b"123456789",
b"hello world",
b"a longer string that will produce some arbitrary crc value",
];
for data in inputs {
let crc = crc32c(data);
let masked = masked_crc32c(data);
assert_eq!(unmask(masked), crc, "round-trip failed for {data:?}");
}
}
}