use std::fs::File;
use std::io::{Read, Seek, SeekFrom, Write};
use std::path::{Path, PathBuf};
use crate::data::{CellArray, DataArray, Points, PolyData};
#[derive(Debug, Clone)]
pub struct MmapDataArray {
pub path: PathBuf,
pub offset: u64,
pub num_tuples: usize,
pub num_components: usize,
}
impl MmapDataArray {
pub fn new(path: impl Into<PathBuf>, offset: u64, num_tuples: usize, num_components: usize) -> Self {
Self { path: path.into(), offset, num_tuples, num_components }
}
pub fn load(&self) -> std::io::Result<DataArray<f64>> {
let total = self.num_tuples * self.num_components;
let byte_len = total * 8;
let mut file = File::open(&self.path)?;
file.seek(SeekFrom::Start(self.offset))?;
let mut buf = vec![0u8; byte_len];
file.read_exact(&mut buf)?;
let data: Vec<f64> = buf
.chunks_exact(8)
.map(|c| f64::from_le_bytes(c.try_into().unwrap()))
.collect();
Ok(DataArray::from_vec("mmap_array", data, self.num_components))
}
}
#[derive(Debug, Clone)]
pub struct MmapPointCloud {
pub path: PathBuf,
pub offset: u64,
pub num_points: usize,
}
impl MmapPointCloud {
pub fn new(path: impl Into<PathBuf>, offset: u64, num_points: usize) -> Self {
Self { path: path.into(), offset, num_points }
}
pub fn load(&self) -> std::io::Result<PolyData> {
let byte_len = self.num_points * 3 * 8;
let mut file = File::open(&self.path)?;
file.seek(SeekFrom::Start(self.offset))?;
let mut buf = vec![0u8; byte_len];
file.read_exact(&mut buf)?;
let mut points = Points::<f64>::new();
let mut verts = CellArray::new();
for i in 0..self.num_points {
let base = i * 3 * 8;
let x = f64::from_le_bytes(buf[base..base + 8].try_into().unwrap());
let y = f64::from_le_bytes(buf[base + 8..base + 16].try_into().unwrap());
let z = f64::from_le_bytes(buf[base + 16..base + 24].try_into().unwrap());
points.push([x, y, z]);
verts.push_cell(&[i as i64]);
}
let mut pd = PolyData::new();
pd.points = points;
pd.verts = verts;
Ok(pd)
}
}
pub fn write_binary_array(path: &Path, array: &DataArray<f64>) -> std::io::Result<()> {
let mut file = File::create(path)?;
for &v in array.as_slice() {
file.write_all(&v.to_le_bytes())?;
}
file.flush()?;
Ok(())
}
pub fn read_binary_array(path: &Path, name: &str, num_components: usize) -> std::io::Result<DataArray<f64>> {
let mut file = File::open(path)?;
let mut buf = Vec::new();
file.read_to_end(&mut buf)?;
let data: Vec<f64> = buf
.chunks_exact(8)
.map(|c| f64::from_le_bytes(c.try_into().unwrap()))
.collect();
Ok(DataArray::from_vec(name, data, num_components))
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Write as _;
#[test]
fn write_read_roundtrip() {
let dir = std::env::temp_dir().join("vtk_mmap_test_roundtrip");
let _ = std::fs::create_dir_all(&dir);
let path = dir.join("array.bin");
let orig = DataArray::<f64>::from_vec("temp", vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0], 3);
write_binary_array(&path, &orig).unwrap();
let loaded = read_binary_array(&path, "temp", 3).unwrap();
assert_eq!(loaded.num_tuples(), 2);
assert_eq!(loaded.num_components(), 3);
assert_eq!(loaded.as_slice(), orig.as_slice());
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn mmap_data_array_load() {
let dir = std::env::temp_dir().join("vtk_mmap_test_load");
let _ = std::fs::create_dir_all(&dir);
let path = dir.join("data.bin");
let mut f = File::create(&path).unwrap();
f.write_all(&[0u8; 16]).unwrap(); for v in &[10.0f64, 20.0, 30.0] {
f.write_all(&v.to_le_bytes()).unwrap();
}
f.flush().unwrap();
let desc = MmapDataArray::new(&path, 16, 3, 1);
let arr = desc.load().unwrap();
assert_eq!(arr.num_tuples(), 3);
assert_eq!(arr.as_slice(), &[10.0, 20.0, 30.0]);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn mmap_point_cloud_load() {
let dir = std::env::temp_dir().join("vtk_mmap_test_pc");
let _ = std::fs::create_dir_all(&dir);
let path = dir.join("points.bin");
let mut f = File::create(&path).unwrap();
for v in &[1.0f64, 2.0, 3.0, 4.0, 5.0, 6.0] {
f.write_all(&v.to_le_bytes()).unwrap();
}
f.flush().unwrap();
let pc = MmapPointCloud::new(&path, 0, 2);
let pd = pc.load().unwrap();
assert_eq!(pd.points.len(), 2);
assert_eq!(pd.verts.num_cells(), 2);
let p0 = pd.points.get(0);
assert!((p0[0] - 1.0).abs() < 1e-12);
assert!((p0[1] - 2.0).abs() < 1e-12);
assert!((p0[2] - 3.0).abs() < 1e-12);
let _ = std::fs::remove_dir_all(&dir);
}
}