use super::frame::ValueRange;
use base64::engine::general_purpose::STANDARD;
use base64::write::EncoderWriter;
use serde::Serialize;
use std::io::{self, Write};
#[derive(Clone, Copy)]
pub(crate) enum BlockData<'a> {
F32(&'a [f32]),
U32(&'a [u32]),
U16(&'a [u16]),
}
impl BlockData<'_> {
pub(crate) fn dtype(&self) -> &'static str {
match self {
BlockData::F32(_) => "f32",
BlockData::U32(_) => "u32",
BlockData::U16(_) => "u16",
}
}
pub(crate) fn byte_len(&self) -> usize {
match self {
BlockData::F32(v) => v.len() * 4,
BlockData::U32(v) => v.len() * 4,
BlockData::U16(v) => v.len() * 2,
}
}
fn write_le<W: Write>(&self, w: &mut W) -> io::Result<()> {
let mut buf = [0u8; 8192];
macro_rules! stream {
($slice:expr, $bytes:expr) => {{
let mut n = 0usize;
for &x in $slice {
let b = x.to_le_bytes();
buf[n..n + $bytes].copy_from_slice(&b);
n += $bytes;
if n == buf.len() {
w.write_all(&buf)?;
n = 0;
}
}
if n > 0 {
w.write_all(&buf[..n])?;
}
}};
}
match self {
BlockData::F32(v) => stream!(*v, 4),
BlockData::U32(v) => stream!(*v, 4),
BlockData::U16(v) => stream!(*v, 2),
}
Ok(())
}
}
pub(crate) struct Block<'a> {
pub(crate) name: &'static str,
pub(crate) shape: Vec<usize>,
pub(crate) data: BlockData<'a>,
}
fn kv<W: Write, T: Serialize + ?Sized>(w: &mut W, key: &str, val: &T) -> io::Result<()> {
serde_json::to_writer(&mut *w, key).map_err(io::Error::other)?;
w.write_all(b":")?;
serde_json::to_writer(&mut *w, val).map_err(io::Error::other)?;
Ok(())
}
pub fn write_json<T: Serialize, W: Write>(bundle: &T, w: &mut W) -> io::Result<()> {
serde_json::to_writer(w, bundle).map_err(io::Error::other)
}
#[allow(clippy::too_many_arguments)]
fn write_head<W: Write>(
w: &mut W,
schema_version: u32,
inputs_ref: &str,
property: &str,
cell_count: usize,
shell_cell_count: usize,
vertex_count: usize,
triangle_count: usize,
zone_names: &[String],
value_range: &impl Serialize,
encoding: &str,
) -> io::Result<()> {
w.write_all(b"{")?;
kv(w, "schema_version", &schema_version)?;
w.write_all(b",")?;
kv(w, "kind", "volume")?;
w.write_all(b",")?;
kv(w, "inputs_ref", inputs_ref)?;
w.write_all(b",")?;
kv(w, "property", property)?;
w.write_all(b",")?;
kv(w, "cell_count", &cell_count)?;
w.write_all(b",")?;
kv(w, "shell_cell_count", &shell_cell_count)?;
w.write_all(b",")?;
kv(w, "vertex_count", &vertex_count)?;
w.write_all(b",")?;
kv(w, "triangle_count", &triangle_count)?;
w.write_all(b",")?;
kv(w, "zone_names", zone_names)?;
w.write_all(b",")?;
kv(w, "value_range", value_range)?;
w.write_all(b",")?;
kv(w, "encoding", encoding)?;
Ok(())
}
pub(crate) struct Head<'a> {
pub(crate) schema_version: u32,
pub(crate) inputs_ref: &'a str,
pub(crate) property: &'a str,
pub(crate) cell_count: usize,
pub(crate) shell_cell_count: usize,
pub(crate) vertex_count: usize,
pub(crate) triangle_count: usize,
pub(crate) zone_names: &'a [String],
pub(crate) value_range: &'a ValueRange,
}
pub(crate) fn write_self_contained<W: Write>(
head: &Head,
blocks: &[Block],
w: &mut W,
) -> io::Result<()> {
write_head(
w,
head.schema_version,
head.inputs_ref,
head.property,
head.cell_count,
head.shell_cell_count,
head.vertex_count,
head.triangle_count,
head.zone_names,
head.value_range,
"base64",
)?;
w.write_all(b",\"blocks\":{")?;
for (n, blk) in blocks.iter().enumerate() {
if n > 0 {
w.write_all(b",")?;
}
serde_json::to_writer(&mut *w, blk.name).map_err(io::Error::other)?;
w.write_all(b":{")?;
kv(w, "dtype", blk.data.dtype())?;
w.write_all(b",")?;
kv(w, "shape", &blk.shape)?;
w.write_all(b",\"data\":\"")?;
{
let mut enc = EncoderWriter::new(&mut *w, &STANDARD);
blk.data.write_le(&mut enc)?;
enc.finish()?;
}
w.write_all(b"\"}")?;
}
w.write_all(b"}}")?;
Ok(())
}
pub(crate) fn write_sidecar<W1: Write, W2: Write>(
head: &Head,
blocks: &[Block],
json: &mut W1,
bin: &mut W2,
) -> io::Result<()> {
write_head(
json,
head.schema_version,
head.inputs_ref,
head.property,
head.cell_count,
head.shell_cell_count,
head.vertex_count,
head.triangle_count,
head.zone_names,
head.value_range,
"sidecar",
)?;
json.write_all(b",\"blocks\":{")?;
let mut offset = 0usize;
for (n, blk) in blocks.iter().enumerate() {
if n > 0 {
json.write_all(b",")?;
}
serde_json::to_writer(&mut *json, blk.name).map_err(io::Error::other)?;
json.write_all(b":{")?;
kv(json, "dtype", blk.data.dtype())?;
json.write_all(b",")?;
kv(json, "shape", &blk.shape)?;
json.write_all(b",")?;
kv(json, "offset", &offset)?;
json.write_all(b",")?;
kv(json, "length", &blk.data.byte_len())?;
json.write_all(b"}")?;
offset += blk.data.byte_len();
}
json.write_all(b"}}")?;
for blk in blocks {
blk.data.write_le(bin)?;
}
Ok(())
}