use crate::error::{Result, SklearsError};
use crate::types::{Array1, Array2, Float};
use std::io::{Read, Write};
use std::path::Path;
#[cfg(feature = "binary")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum BinaryFormat {
Bincode,
MessagePack,
Custom,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum CompressionType {
None,
#[cfg(feature = "compression")]
Gzip,
#[cfg(feature = "compression")]
Zstd,
}
#[derive(Debug, Clone)]
pub struct BinaryConfig {
pub format: BinaryFormat,
pub compression: CompressionType,
pub include_metadata: bool,
pub version: u32,
pub little_endian: bool,
}
impl Default for BinaryConfig {
fn default() -> Self {
Self {
format: BinaryFormat::Bincode,
compression: CompressionType::None,
include_metadata: true,
version: 1,
little_endian: true,
}
}
}
#[cfg(feature = "binary")]
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BinaryMetadata {
pub version: u32,
pub created_at: u64,
pub data_type: String,
pub shape: Option<Vec<usize>>,
pub uncompressed_size: Option<u64>,
pub compression: String,
pub little_endian: bool,
pub user_metadata: std::collections::HashMap<String, String>,
}
#[cfg(feature = "binary")]
impl Default for BinaryMetadata {
fn default() -> Self {
Self {
version: 1,
created_at: std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs(),
data_type: "unknown".to_string(),
shape: None,
uncompressed_size: None,
compression: "none".to_string(),
little_endian: true,
user_metadata: std::collections::HashMap::new(),
}
}
}
pub trait BinarySerialize {
fn serialize_binary(&self, config: &BinaryConfig) -> Result<Vec<u8>>;
fn serialize_binary_to_writer<W: Write>(&self, writer: W, config: &BinaryConfig) -> Result<()>;
fn serialize_binary_to_file<P: AsRef<Path>>(
&self,
path: P,
config: &BinaryConfig,
) -> Result<()>;
}
pub trait BinaryDeserialize: Sized {
fn deserialize_binary(data: &[u8], config: &BinaryConfig) -> Result<Self>;
fn deserialize_binary_from_reader<R: Read>(reader: R, config: &BinaryConfig) -> Result<Self>;
fn deserialize_binary_from_file<P: AsRef<Path>>(path: P, config: &BinaryConfig)
-> Result<Self>;
}
pub struct BinarySerializer {
config: BinaryConfig,
}
impl BinarySerializer {
pub fn new(config: BinaryConfig) -> Self {
Self { config }
}
pub fn bincode() -> Self {
Self::new(BinaryConfig {
format: BinaryFormat::Bincode,
..Default::default()
})
}
pub fn messagepack() -> Self {
Self::new(BinaryConfig {
format: BinaryFormat::MessagePack,
..Default::default()
})
}
pub fn custom() -> Self {
Self::new(BinaryConfig {
format: BinaryFormat::Custom,
..Default::default()
})
}
#[cfg(feature = "binary")]
pub fn serialize<T: Serialize>(&self, data: &T) -> Result<Vec<u8>> {
let serialized = match self.config.format {
BinaryFormat::Bincode => {
oxicode::serde::encode_to_vec(data, oxicode::config::standard()).map_err(|e| {
SklearsError::SerializationError(format!("Oxicode serialization failed: {}", e))
})?
}
BinaryFormat::MessagePack => {
#[cfg(feature = "messagepack")]
{
rmp_serde::to_vec(data).map_err(|e| {
SklearsError::SerializationError(format!(
"MessagePack serialization failed: {}",
e
))
})?
}
#[cfg(not(feature = "messagepack"))]
{
return Err(SklearsError::MissingDependency {
dependency: "rmp-serde".to_string(),
feature: "MessagePack serialization".to_string(),
});
}
}
BinaryFormat::Custom => {
oxicode::serde::encode_to_vec(data, oxicode::config::standard()).map_err(|e| {
SklearsError::SerializationError(format!("Custom serialization failed: {}", e))
})?
}
};
self.apply_compression(serialized)
}
#[cfg(feature = "binary")]
pub fn deserialize<T: for<'de> Deserialize<'de>>(&self, data: &[u8]) -> Result<T> {
let decompressed = self.decompress(data)?;
match self.config.format {
BinaryFormat::Bincode => {
let (value, _bytes_read) =
oxicode::serde::decode_from_slice(&decompressed, oxicode::config::standard())
.map_err(|e| {
SklearsError::DeserializationError(format!(
"Oxicode deserialization failed: {}",
e
))
})?;
Ok(value)
}
BinaryFormat::MessagePack => {
#[cfg(feature = "messagepack")]
{
rmp_serde::from_slice(&decompressed).map_err(|e| {
SklearsError::DeserializationError(format!(
"MessagePack deserialization failed: {}",
e
))
})
}
#[cfg(not(feature = "messagepack"))]
{
Err(SklearsError::MissingDependency {
dependency: "rmp-serde".to_string(),
feature: "MessagePack deserialization".to_string(),
})
}
}
BinaryFormat::Custom => {
let (value, _bytes_read) =
oxicode::serde::decode_from_slice(&decompressed, oxicode::config::standard())
.map_err(|e| {
SklearsError::DeserializationError(format!(
"Custom deserialization failed: {}",
e
))
})?;
Ok(value)
}
}
}
fn apply_compression(&self, data: Vec<u8>) -> Result<Vec<u8>> {
match self.config.compression {
CompressionType::None => Ok(data),
#[cfg(feature = "compression")]
CompressionType::Gzip => oxiarc_deflate::gzip_compress(&data, 6).map_err(|e| {
SklearsError::SerializationError(format!("Gzip compression failed: {}", e))
}),
#[cfg(feature = "compression")]
CompressionType::Zstd => oxiarc_zstd::encode_all(&data[..], 3).map_err(|e| {
SklearsError::SerializationError(format!("Zstd compression failed: {}", e))
}),
#[cfg(not(feature = "compression"))]
_ => Err(SklearsError::MissingDependency {
dependency: "compression".to_string(),
feature: "data compression".to_string(),
}),
}
}
fn decompress(&self, data: &[u8]) -> Result<Vec<u8>> {
match self.config.compression {
CompressionType::None => Ok(data.to_vec()),
#[cfg(feature = "compression")]
CompressionType::Gzip => oxiarc_deflate::gzip_decompress(data).map_err(|e| {
SklearsError::DeserializationError(format!("Gzip decompression failed: {}", e))
}),
#[cfg(feature = "compression")]
CompressionType::Zstd => oxiarc_zstd::decode_all(data).map_err(|e| {
SklearsError::DeserializationError(format!("Zstd decompression failed: {}", e))
}),
#[cfg(not(feature = "compression"))]
_ => Err(SklearsError::MissingDependency {
dependency: "compression".to_string(),
feature: "data decompression".to_string(),
}),
}
}
}
pub struct ArrayBinaryFormat;
impl ArrayBinaryFormat {
pub fn serialize_array2(array: &Array2<Float>) -> Result<Vec<u8>> {
let (rows, cols) = array.dim();
let mut buffer = Vec::with_capacity(24 + rows * cols * 8);
buffer.extend_from_slice(b"SKLA"); buffer.extend_from_slice(&1u32.to_le_bytes()); buffer.extend_from_slice(&(rows as u64).to_le_bytes()); buffer.extend_from_slice(&(cols as u64).to_le_bytes());
for row in array.rows() {
for &value in row {
buffer.extend_from_slice(&value.to_le_bytes());
}
}
Ok(buffer)
}
pub fn deserialize_array2(data: &[u8]) -> Result<Array2<Float>> {
if data.len() < 24 {
return Err(SklearsError::DeserializationError(
"Insufficient data for array header".to_string(),
));
}
if &data[0..4] != b"SKLA" {
return Err(SklearsError::DeserializationError(
"Invalid magic number".to_string(),
));
}
let version = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
if version != 1 {
return Err(SklearsError::DeserializationError(format!(
"Unsupported version: {}",
version
)));
}
let rows = u64::from_le_bytes([
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
]) as usize;
let cols = u64::from_le_bytes([
data[16], data[17], data[18], data[19], data[20], data[21], data[22], data[23],
]) as usize;
let expected_len = 24 + rows * cols * 8;
if data.len() != expected_len {
return Err(SklearsError::DeserializationError(format!(
"Data length mismatch: expected {}, got {}",
expected_len,
data.len()
)));
}
let mut array_data = Vec::with_capacity(rows * cols);
let data_start = 24;
for i in 0..(rows * cols) {
let start = data_start + i * 8;
let end = start + 8;
let bytes: [u8; 8] = data[start..end].try_into().map_err(|_| {
SklearsError::DeserializationError("Failed to read float bytes".to_string())
})?;
array_data.push(Float::from_le_bytes(bytes));
}
Array2::from_shape_vec((rows, cols), array_data).map_err(|e| {
SklearsError::DeserializationError(format!("Failed to create array: {}", e))
})
}
pub fn serialize_array1(array: &Array1<Float>) -> Result<Vec<u8>> {
let len = array.len();
let mut buffer = Vec::with_capacity(16 + len * 8);
buffer.extend_from_slice(b"SKL1"); buffer.extend_from_slice(&1u32.to_le_bytes()); buffer.extend_from_slice(&(len as u64).to_le_bytes());
for &value in array {
buffer.extend_from_slice(&value.to_le_bytes());
}
Ok(buffer)
}
pub fn deserialize_array1(data: &[u8]) -> Result<Array1<Float>> {
if data.len() < 16 {
return Err(SklearsError::DeserializationError(
"Insufficient data for array header".to_string(),
));
}
if &data[0..4] != b"SKL1" {
return Err(SklearsError::DeserializationError(
"Invalid magic number for 1D array".to_string(),
));
}
let version = u32::from_le_bytes([data[4], data[5], data[6], data[7]]);
if version != 1 {
return Err(SklearsError::DeserializationError(format!(
"Unsupported version: {}",
version
)));
}
let len = u64::from_le_bytes([
data[8], data[9], data[10], data[11], data[12], data[13], data[14], data[15],
]) as usize;
let expected_len = 16 + len * 8;
if data.len() != expected_len {
return Err(SklearsError::DeserializationError(format!(
"Data length mismatch: expected {}, got {}",
expected_len,
data.len()
)));
}
let mut array_data = Vec::with_capacity(len);
let data_start = 16;
for i in 0..len {
let start = data_start + i * 8;
let end = start + 8;
let bytes: [u8; 8] = data[start..end].try_into().map_err(|_| {
SklearsError::DeserializationError("Failed to read float bytes".to_string())
})?;
array_data.push(Float::from_le_bytes(bytes));
}
Ok(Array1::from_vec(array_data))
}
}
pub struct StreamingBinaryWriter<W: Write> {
writer: W,
config: BinaryConfig,
#[cfg(feature = "binary")]
metadata: BinaryMetadata,
}
impl<W: Write> StreamingBinaryWriter<W> {
#[cfg(feature = "binary")]
pub fn new(writer: W, config: BinaryConfig) -> Result<Self> {
let metadata = BinaryMetadata::default();
let mut instance = Self {
writer,
config,
metadata,
};
if instance.config.include_metadata {
instance.write_metadata()?;
}
Ok(instance)
}
#[cfg(feature = "binary")]
fn write_metadata(&mut self) -> Result<()> {
let serializer = BinarySerializer::new(self.config.clone());
let metadata_bytes = serializer.serialize(&self.metadata)?;
let metadata_len = metadata_bytes.len() as u64;
self.writer
.write_all(&metadata_len.to_le_bytes())
.map_err(|e| {
SklearsError::SerializationError(format!("Failed to write metadata length: {}", e))
})?;
self.writer.write_all(&metadata_bytes).map_err(|e| {
SklearsError::SerializationError(format!("Failed to write metadata: {}", e))
})?;
Ok(())
}
#[cfg(feature = "binary")]
pub fn write_chunk<T: Serialize>(&mut self, chunk: &T) -> Result<()> {
let serializer = BinarySerializer::new(self.config.clone());
let chunk_bytes = serializer.serialize(chunk)?;
let chunk_len = chunk_bytes.len() as u64;
self.writer
.write_all(&chunk_len.to_le_bytes())
.map_err(|e| {
SklearsError::SerializationError(format!("Failed to write chunk length: {}", e))
})?;
self.writer.write_all(&chunk_bytes).map_err(|e| {
SklearsError::SerializationError(format!("Failed to write chunk: {}", e))
})?;
Ok(())
}
pub fn finish(mut self) -> Result<W> {
self.writer.flush().map_err(|e| {
SklearsError::SerializationError(format!("Failed to flush writer: {}", e))
})?;
Ok(self.writer)
}
}
pub struct StreamingBinaryReader<R: Read> {
reader: R,
config: BinaryConfig,
#[cfg(feature = "binary")]
metadata: Option<BinaryMetadata>,
}
impl<R: Read> StreamingBinaryReader<R> {
#[cfg(feature = "binary")]
pub fn new(mut reader: R, config: BinaryConfig) -> Result<Self> {
let metadata = if config.include_metadata {
Some(Self::read_metadata(&mut reader, &config)?)
} else {
None
};
Ok(Self {
reader,
config,
metadata,
})
}
#[cfg(feature = "binary")]
fn read_metadata(reader: &mut R, config: &BinaryConfig) -> Result<BinaryMetadata> {
let mut len_bytes = [0u8; 8];
reader.read_exact(&mut len_bytes).map_err(|e| {
SklearsError::DeserializationError(format!("Failed to read metadata length: {}", e))
})?;
let metadata_len = u64::from_le_bytes(len_bytes) as usize;
let mut metadata_bytes = vec![0u8; metadata_len];
reader.read_exact(&mut metadata_bytes).map_err(|e| {
SklearsError::DeserializationError(format!("Failed to read metadata: {}", e))
})?;
let deserializer = BinarySerializer::new(config.clone());
deserializer.deserialize(&metadata_bytes)
}
#[cfg(feature = "binary")]
pub fn read_chunk<T: for<'de> Deserialize<'de>>(&mut self) -> Result<Option<T>> {
let mut len_bytes = [0u8; 8];
match self.reader.read_exact(&mut len_bytes) {
Ok(_) => {}
Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
return Ok(None); }
Err(e) => {
return Err(SklearsError::DeserializationError(format!(
"Failed to read chunk length: {}",
e
)));
}
}
let chunk_len = u64::from_le_bytes(len_bytes) as usize;
let mut chunk_bytes = vec![0u8; chunk_len];
self.reader.read_exact(&mut chunk_bytes).map_err(|e| {
SklearsError::DeserializationError(format!("Failed to read chunk: {}", e))
})?;
let deserializer = BinarySerializer::new(self.config.clone());
let chunk = deserializer.deserialize(&chunk_bytes)?;
Ok(Some(chunk))
}
#[cfg(feature = "binary")]
pub fn metadata(&self) -> Option<&BinaryMetadata> {
self.metadata.as_ref()
}
}
pub struct BinaryFileStorage;
impl BinaryFileStorage {
#[cfg(feature = "binary")]
pub fn save<T: Serialize, P: AsRef<Path>>(
data: &T,
path: P,
config: &BinaryConfig,
) -> Result<()> {
let file = std::fs::File::create(&path).map_err(|e| {
SklearsError::FileError(format!(
"Failed to create file {}: {}",
path.as_ref().display(),
e
))
})?;
let mut writer = StreamingBinaryWriter::new(file, config.clone())?;
writer.write_chunk(data)?;
writer.finish()?;
Ok(())
}
#[cfg(feature = "binary")]
pub fn load<T: for<'de> Deserialize<'de>, P: AsRef<Path>>(
path: P,
config: &BinaryConfig,
) -> Result<T> {
let file = std::fs::File::open(&path).map_err(|e| {
SklearsError::FileError(format!(
"Failed to open file {}: {}",
path.as_ref().display(),
e
))
})?;
let mut reader = StreamingBinaryReader::new(file, config.clone())?;
reader
.read_chunk()?
.ok_or_else(|| SklearsError::DeserializationError("No data found in file".to_string()))
}
#[cfg(feature = "binary")]
pub fn info<P: AsRef<Path>>(path: P, config: &BinaryConfig) -> Result<BinaryMetadata> {
let file = std::fs::File::open(&path).map_err(|e| {
SklearsError::FileError(format!(
"Failed to open file {}: {}",
path.as_ref().display(),
e
))
})?;
let reader = StreamingBinaryReader::new(file, config.clone())?;
reader.metadata().cloned().ok_or_else(|| {
SklearsError::DeserializationError("No metadata found in file".to_string())
})
}
}
#[cfg(feature = "binary")]
impl BinarySerialize for Array2<Float> {
fn serialize_binary(&self, config: &BinaryConfig) -> Result<Vec<u8>> {
match config.format {
BinaryFormat::Custom => ArrayBinaryFormat::serialize_array2(self),
_ => {
let serializer = BinarySerializer::new(config.clone());
serializer.serialize(self)
}
}
}
fn serialize_binary_to_writer<W: Write>(
&self,
mut writer: W,
config: &BinaryConfig,
) -> Result<()> {
let data = self.serialize_binary(config)?;
writer.write_all(&data).map_err(|e| {
SklearsError::SerializationError(format!("Failed to write to writer: {}", e))
})
}
fn serialize_binary_to_file<P: AsRef<Path>>(
&self,
path: P,
config: &BinaryConfig,
) -> Result<()> {
let file = std::fs::File::create(&path).map_err(|e| {
SklearsError::FileError(format!(
"Failed to create file {}: {}",
path.as_ref().display(),
e
))
})?;
self.serialize_binary_to_writer(file, config)
}
}
#[cfg(feature = "binary")]
impl BinaryDeserialize for Array2<Float> {
fn deserialize_binary(data: &[u8], config: &BinaryConfig) -> Result<Self> {
match config.format {
BinaryFormat::Custom => ArrayBinaryFormat::deserialize_array2(data),
_ => {
let serializer = BinarySerializer::new(config.clone());
serializer.deserialize(data)
}
}
}
fn deserialize_binary_from_reader<R: Read>(
mut reader: R,
config: &BinaryConfig,
) -> Result<Self> {
let mut data = Vec::new();
reader.read_to_end(&mut data).map_err(|e| {
SklearsError::DeserializationError(format!("Failed to read from reader: {}", e))
})?;
Self::deserialize_binary(&data, config)
}
fn deserialize_binary_from_file<P: AsRef<Path>>(
path: P,
config: &BinaryConfig,
) -> Result<Self> {
let file = std::fs::File::open(&path).map_err(|e| {
SklearsError::FileError(format!(
"Failed to open file {}: {}",
path.as_ref().display(),
e
))
})?;
Self::deserialize_binary_from_reader(file, config)
}
}
#[cfg(feature = "binary")]
impl BinarySerialize for Array1<Float> {
fn serialize_binary(&self, config: &BinaryConfig) -> Result<Vec<u8>> {
match config.format {
BinaryFormat::Custom => ArrayBinaryFormat::serialize_array1(self),
_ => {
let serializer = BinarySerializer::new(config.clone());
serializer.serialize(self)
}
}
}
fn serialize_binary_to_writer<W: Write>(
&self,
mut writer: W,
config: &BinaryConfig,
) -> Result<()> {
let data = self.serialize_binary(config)?;
writer.write_all(&data).map_err(|e| {
SklearsError::SerializationError(format!("Failed to write to writer: {}", e))
})
}
fn serialize_binary_to_file<P: AsRef<Path>>(
&self,
path: P,
config: &BinaryConfig,
) -> Result<()> {
let file = std::fs::File::create(&path).map_err(|e| {
SklearsError::FileError(format!(
"Failed to create file {}: {}",
path.as_ref().display(),
e
))
})?;
self.serialize_binary_to_writer(file, config)
}
}
#[cfg(feature = "binary")]
impl BinaryDeserialize for Array1<Float> {
fn deserialize_binary(data: &[u8], config: &BinaryConfig) -> Result<Self> {
match config.format {
BinaryFormat::Custom => ArrayBinaryFormat::deserialize_array1(data),
_ => {
let serializer = BinarySerializer::new(config.clone());
serializer.deserialize(data)
}
}
}
fn deserialize_binary_from_reader<R: Read>(
mut reader: R,
config: &BinaryConfig,
) -> Result<Self> {
let mut data = Vec::new();
reader.read_to_end(&mut data).map_err(|e| {
SklearsError::DeserializationError(format!("Failed to read from reader: {}", e))
})?;
Self::deserialize_binary(&data, config)
}
fn deserialize_binary_from_file<P: AsRef<Path>>(
path: P,
config: &BinaryConfig,
) -> Result<Self> {
let file = std::fs::File::open(&path).map_err(|e| {
SklearsError::FileError(format!(
"Failed to open file {}: {}",
path.as_ref().display(),
e
))
})?;
Self::deserialize_binary_from_reader(file, config)
}
}
pub mod convenience {
use super::*;
pub fn save_array2<P: AsRef<Path>>(array: &Array2<Float>, path: P) -> Result<()> {
let config = BinaryConfig {
format: BinaryFormat::Custom,
compression: CompressionType::None,
include_metadata: true,
..Default::default()
};
array.serialize_binary_to_file(path, &config)
}
pub fn load_array2<P: AsRef<Path>>(path: P) -> Result<Array2<Float>> {
let config = BinaryConfig {
format: BinaryFormat::Custom,
..Default::default()
};
Array2::deserialize_binary_from_file(path, &config)
}
pub fn save_array1<P: AsRef<Path>>(array: &Array1<Float>, path: P) -> Result<()> {
let config = BinaryConfig {
format: BinaryFormat::Custom,
compression: CompressionType::None,
include_metadata: true,
..Default::default()
};
array.serialize_binary_to_file(path, &config)
}
pub fn load_array1<P: AsRef<Path>>(path: P) -> Result<Array1<Float>> {
let config = BinaryConfig {
format: BinaryFormat::Custom,
..Default::default()
};
Array1::deserialize_binary_from_file(path, &config)
}
#[cfg(feature = "compression")]
pub fn save_array2_compressed<P: AsRef<Path>>(array: &Array2<Float>, path: P) -> Result<()> {
let config = BinaryConfig {
format: BinaryFormat::Custom,
compression: CompressionType::Zstd,
include_metadata: true,
..Default::default()
};
array.serialize_binary_to_file(path, &config)
}
#[cfg(feature = "compression")]
pub fn load_array2_compressed<P: AsRef<Path>>(path: P) -> Result<Array2<Float>> {
let config = BinaryConfig {
format: BinaryFormat::Custom,
compression: CompressionType::Zstd,
..Default::default()
};
Array2::deserialize_binary_from_file(path, &config)
}
}
#[allow(non_snake_case)]
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_array_binary_format() {
let array = Array2::from_shape_vec((3, 2), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.expect("valid array shape");
let bytes = ArrayBinaryFormat::serialize_array2(&array).expect("expected valid value");
assert!(bytes.len() > 24);
let restored = ArrayBinaryFormat::deserialize_array2(&bytes).expect("expected valid value");
assert_eq!(restored.dim(), array.dim());
for ((i, j), &original) in array.indexed_iter() {
assert!((restored[[i, j]] - original).abs() < 1e-10);
}
}
#[test]
fn test_array1_binary_format() {
let array = Array1::from_vec(vec![1.0, 2.0, 3.0, 4.0, 5.0]);
let bytes = ArrayBinaryFormat::serialize_array1(&array).expect("expected valid value");
assert!(bytes.len() > 16);
let restored = ArrayBinaryFormat::deserialize_array1(&bytes).expect("expected valid value");
assert_eq!(restored.len(), array.len());
for (i, &original) in array.iter().enumerate() {
assert!((restored[i] - original).abs() < 1e-10);
}
}
#[cfg(feature = "binary")]
#[test]
fn test_binary_serializer() {
let data = vec![1.0f64, 2.0, 3.0, 4.0];
let config = BinaryConfig::default();
let serializer = BinarySerializer::new(config);
let bytes = serializer
.serialize(&data)
.expect("serialize should succeed");
assert!(!bytes.is_empty());
let restored: Vec<f64> = serializer
.deserialize(&bytes)
.expect("deserialize should succeed");
assert_eq!(restored, data);
}
#[test]
fn test_binary_format_types() {
assert_eq!(BinaryFormat::Bincode, BinaryFormat::Bincode);
assert_ne!(BinaryFormat::Bincode, BinaryFormat::MessagePack);
let config = BinaryConfig::default();
assert_eq!(config.format, BinaryFormat::Bincode);
assert_eq!(config.version, 1);
assert!(config.include_metadata);
}
#[cfg(feature = "binary")]
#[test]
fn test_array_binary_serialize_traits() {
let array = Array2::from_shape_vec((2, 3), vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0])
.expect("valid array shape");
let config = BinaryConfig {
format: BinaryFormat::Custom,
..Default::default()
};
let bytes = array
.serialize_binary(&config)
.expect("serialize_binary should succeed");
assert!(!bytes.is_empty());
let restored = Array2::deserialize_binary(&bytes, &config).expect("expected valid value");
assert_eq!(restored.dim(), array.dim());
for ((i, j), &original) in array.indexed_iter() {
assert!((restored[[i, j]] - original).abs() < 1e-10);
}
}
#[test]
fn test_convenience_functions() {
use tempfile::NamedTempFile;
let array =
Array2::from_shape_vec((2, 2), vec![1.0, 2.0, 3.0, 4.0]).expect("valid array shape");
let temp_file = NamedTempFile::new().expect("failed to create temp file");
let path = temp_file.path();
convenience::save_array2(&array, path).expect("expected valid value");
let loaded = convenience::load_array2(path).expect("expected valid value");
assert_eq!(loaded.dim(), array.dim());
for ((i, j), &original) in array.indexed_iter() {
assert!((loaded[[i, j]] - original).abs() < 1e-10);
}
}
#[test]
fn test_array1_convenience() {
use tempfile::NamedTempFile;
let array = Array1::from_vec(vec![10.0, 20.0, 30.0]);
let temp_file = NamedTempFile::new().expect("failed to create temp file");
let path = temp_file.path();
convenience::save_array1(&array, path).expect("expected valid value");
let loaded = convenience::load_array1(path).expect("expected valid value");
assert_eq!(loaded.len(), array.len());
for (i, &original) in array.iter().enumerate() {
assert!((loaded[i] - original).abs() < 1e-10);
}
}
#[test]
fn test_binary_metadata() {
#[cfg(feature = "binary")]
{
let metadata = BinaryMetadata::default();
assert_eq!(metadata.version, 1);
assert!(metadata.little_endian);
assert_eq!(metadata.compression, "none");
}
}
#[test]
fn test_compression_types() {
assert_eq!(CompressionType::None, CompressionType::None);
#[cfg(feature = "compression")]
{
assert_ne!(CompressionType::None, CompressionType::Gzip);
assert_ne!(CompressionType::Gzip, CompressionType::Zstd);
}
}
}