use serde::{Deserialize, Serialize};
use super::PortableError;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum DType {
F64,
U8,
}
impl DType {
fn size(self) -> usize {
match self {
DType::F64 => 8,
DType::U8 => 1,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Accessor {
pub dtype: DType,
pub count: usize,
pub offset: usize,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ArrF64 {
Acc {
acc: usize,
},
Inline(Vec<f64>),
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
#[serde(untagged)]
pub enum ArrU8 {
Acc {
acc: usize,
},
Inline(Vec<u8>),
}
#[cfg(feature = "portable")]
#[derive(Debug, Default)]
pub(crate) struct BankWriter {
pub(crate) bytes: Vec<u8>,
pub(crate) accessors: Vec<Accessor>,
}
#[cfg(feature = "portable")]
impl BankWriter {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn push_f64(&mut self, values: &[f64]) -> ArrF64 {
self.align_to(8);
let offset = self.bytes.len();
for v in values {
self.bytes.extend_from_slice(&v.to_le_bytes());
}
self.accessors.push(Accessor {
dtype: DType::F64,
count: values.len(),
offset,
});
ArrF64::Acc {
acc: self.accessors.len() - 1,
}
}
pub(crate) fn push_pairs(&mut self, points: &[[f64; 2]]) -> ArrF64 {
self.align_to(8);
let offset = self.bytes.len();
for [x, y] in points {
self.bytes.extend_from_slice(&x.to_le_bytes());
self.bytes.extend_from_slice(&y.to_le_bytes());
}
self.accessors.push(Accessor {
dtype: DType::F64,
count: points.len() * 2,
offset,
});
ArrF64::Acc {
acc: self.accessors.len() - 1,
}
}
pub(crate) fn push_u8(&mut self, values: &[u8]) -> ArrU8 {
let offset = self.bytes.len();
self.bytes.extend_from_slice(values);
self.accessors.push(Accessor {
dtype: DType::U8,
count: values.len(),
offset,
});
ArrU8::Acc {
acc: self.accessors.len() - 1,
}
}
fn align_to(&mut self, align: usize) {
while self.bytes.len() % align != 0 {
self.bytes.push(0);
}
}
}
#[cfg(feature = "portable")]
#[derive(Debug, Clone, Copy)]
pub(crate) struct BankReader<'a> {
bin: &'a [u8],
accessors: &'a [Accessor],
}
#[cfg(feature = "portable")]
impl<'a> BankReader<'a> {
pub(crate) fn new(bin: &'a [u8], accessors: &'a [Accessor]) -> Self {
Self { bin, accessors }
}
fn slice(&self, index: usize, dtype: DType) -> Result<&'a [u8], PortableError> {
let acc = self.accessors.get(index).ok_or_else(|| {
PortableError::Malformed(format!(
"accessor index {index} out of range (table has {})",
self.accessors.len()
))
})?;
if acc.dtype != dtype {
return Err(PortableError::Malformed(format!(
"accessor {index} has dtype {:?}, expected {dtype:?}",
acc.dtype
)));
}
let len = acc
.count
.checked_mul(acc.dtype.size())
.ok_or_else(|| PortableError::Malformed(format!("accessor {index} size overflows")))?;
let end = acc.offset.checked_add(len).ok_or_else(|| {
PortableError::Malformed(format!("accessor {index} extent overflows"))
})?;
self.bin.get(acc.offset..end).ok_or_else(|| {
PortableError::Malformed(format!(
"accessor {index} spans bytes {}..{end} but the binary chunk holds {}",
acc.offset,
self.bin.len()
))
})
}
pub(crate) fn f64s(&self, arr: &ArrF64) -> Result<Vec<f64>, PortableError> {
match arr {
ArrF64::Inline(values) => Ok(values.clone()),
ArrF64::Acc { acc } => {
let bytes = self.slice(*acc, DType::F64)?;
Ok(bytes
.chunks_exact(8)
.map(|c| f64::from_le_bytes(c.try_into().expect("chunks_exact(8)")))
.collect())
}
}
}
pub(crate) fn pairs(&self, arr: &ArrF64) -> Result<Vec<[f64; 2]>, PortableError> {
let flat = self.f64s(arr)?;
if flat.len() % 2 != 0 {
return Err(PortableError::Malformed(format!(
"point array has odd element count {}",
flat.len()
)));
}
Ok(flat.chunks_exact(2).map(|c| [c[0], c[1]]).collect())
}
pub(crate) fn u8s(&self, arr: &ArrU8) -> Result<Vec<u8>, PortableError> {
match arr {
ArrU8::Inline(values) => Ok(values.clone()),
ArrU8::Acc { acc } => Ok(self.slice(*acc, DType::U8)?.to_vec()),
}
}
}