#![warn(missing_docs)]
extern crate self as wolfram_serialize;
pub mod complex;
pub mod constants;
pub mod errors;
pub mod from_wxf;
pub mod numeric_in;
pub mod reader;
pub mod strategy;
pub mod to_wxf;
pub mod writer;
pub mod wxf;
pub use crate::errors::Error;
pub use crate::complex::{Complex, Complex32, Complex64};
pub use crate::constants::{
ExpressionEnum, HeaderEnum, NumericArrayEnum, PackedArrayEnum,
};
pub use crate::from_wxf::FromWXF;
pub use crate::reader::{Reader, SliceReader};
pub use crate::to_wxf::{ToWXF, WxfStruct};
pub use crate::writer::Writer;
pub use crate::wxf::reader::WxfReader;
pub use crate::wxf::writer::WxfWriter;
pub use wolfram_serialize_macros::{Failure, FromWXF, ToWXF};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum CompressionLevel {
Fastest,
Default,
Best,
Level(u8),
}
impl CompressionLevel {
pub(crate) fn to_u8(self) -> u8 {
match self {
CompressionLevel::Fastest => 1,
CompressionLevel::Default => 6,
CompressionLevel::Best => 9,
CompressionLevel::Level(n) => n.min(9),
}
}
}
pub fn to_wxf<T: ToWXF + ?Sized>(
value: &T,
compression: impl Into<Option<CompressionLevel>>,
) -> Result<Vec<u8>, Error> {
use crate::constants::HeaderEnum;
let ver = HeaderEnum::Version as u8;
let sep = HeaderEnum::Separator as u8;
match compression.into() {
None => {
let out = vec![ver, sep];
let mut w = WxfWriter::new(out);
value.to_wxf(&mut w)?;
Ok(w.into_inner())
},
Some(level) => {
use flate2::write::ZlibEncoder;
use flate2::Compression;
let out = vec![ver, HeaderEnum::Compress as u8, sep];
let encoder =
ZlibEncoder::new(out, Compression::new(u32::from(level.to_u8())));
let mut w = WxfWriter::new(encoder);
value.to_wxf(&mut w)?;
Ok(w.into_inner().finish()?)
},
}
}
fn strip_header(bytes: &[u8]) -> Result<std::borrow::Cow<'_, [u8]>, Error> {
use std::io::Read;
use crate::constants::HeaderEnum;
if bytes.len() < 2 {
return Err(Error::invalid(
"byte stream too short for WXF header".into(),
));
}
if bytes[0] != HeaderEnum::Version as u8 {
return Err(Error::invalid(format!(
"WXF header version mismatch: expected {:?}, got {:?}",
HeaderEnum::Version as u8 as char,
bytes[0] as char
)));
}
if bytes[1] == HeaderEnum::Compress as u8 {
if bytes.len() < 3 || bytes[2] != HeaderEnum::Separator as u8 {
return Err(Error::invalid("WXF compressed header truncated".into()));
}
let mut decoded = Vec::new();
flate2::read::ZlibDecoder::new(&bytes[3..])
.read_to_end(&mut decoded)
.map_err(|e| Error::invalid(format!("zlib decompress failed: {}", e)))?;
Ok(std::borrow::Cow::Owned(decoded))
} else if bytes[1] == HeaderEnum::Separator as u8 {
Ok(std::borrow::Cow::Borrowed(&bytes[2..]))
} else {
Err(Error::invalid(format!(
"WXF header separator mismatch: expected ':' or 'C', got {:?}",
bytes[1] as char
)))
}
}
pub fn read_wxf<T>(
bytes: &[u8],
f: impl for<'a> FnOnce(&mut WxfReader<SliceReader<'a>>) -> Result<T, Error>,
) -> Result<T, Error> {
let payload = strip_header(bytes)?;
let mut r = WxfReader::new(SliceReader::new(&payload));
f(&mut r)
}
pub fn from_wxf<T: for<'de> FromWXF<'de>>(bytes: &[u8]) -> Result<T, Error> {
read_wxf(bytes, |r| T::from_wxf(r))
}
pub fn from_wxf_ref<'de, T: FromWXF<'de>>(bytes: &'de [u8]) -> Result<T, Error> {
use crate::constants::HeaderEnum;
if bytes.len() < 2 || bytes[0] != HeaderEnum::Version as u8 {
return Err(Error::invalid("not a WXF stream".into()));
}
if bytes[1] == HeaderEnum::Compress as u8 {
return Err(Error::invalid(
"from_wxf_ref requires uncompressed (8:) WXF — borrowed views can't \
point into a decompressed buffer"
.into(),
));
}
if bytes[1] != HeaderEnum::Separator as u8 {
return Err(Error::invalid("malformed WXF header".into()));
}
let payload: &'de [u8] = &bytes[2..];
let mut r = WxfReader::new(SliceReader::new(payload));
T::from_wxf(&mut r)
}