use crate::{AutoDelaminate, AutoLaminate, ByteLamination};
use std::borrow::Cow;
use std::cmp::max;
use std::error::Error;
use std::marker::PhantomData;
#[derive(Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)]
pub struct Zstd<'a, T> {
bytes: Cow<'a, [u8]>,
_marker: PhantomData<T>,
}
impl<'a, T> From<Cow<'a, [u8]>> for Zstd<'a, T> {
fn from(bytes: Cow<'a, [u8]>) -> Self {
Self {
bytes,
_marker: PhantomData::default(),
}
}
}
impl<'a, T> Into<Cow<'a, [u8]>> for Zstd<'a, T> {
fn into(self) -> Cow<'a, [u8]> {
self.bytes
}
}
impl<'a, T> ByteLamination<'a> for Zstd<'a, T> {
fn as_cow_bytes(&self) -> Cow<'_, [u8]> {
Cow::Borrowed(&self.bytes)
}
fn into_bytes(self) -> Vec<u8> {
self.bytes.into_owned()
}
fn try_from_bytes(bytes: Cow<'a, [u8]>) -> Result<Self, Box<dyn Error>> {
Ok(Self::from(bytes))
}
}
pub const DEFAULT_ZSTD_LEVEL: i32 = 12;
pub const DEFAULT_MEMORY_LIMIT_MULTIPLIER: usize = 16;
pub const DEFAULT_MEMORY_LIMIT_MINIMUM: usize = 1024 * 1024 * 1024;
impl<T> Zstd<'static, T> {
pub fn compress(bytes: &[u8], level: i32) -> Result<Self, Box<dyn Error>> {
let compresed_vec = zstd::bulk::compress(bytes, level)?;
Ok(Self::from(Cow::Owned(compresed_vec)))
}
}
impl<'a, T> Zstd<'a, T> {
pub fn decompress(&self, memory_limit: usize) -> Result<Vec<u8>, Box<dyn Error>> {
let decompressed_vec = zstd::bulk::decompress(&self.bytes, memory_limit)?;
Ok(decompressed_vec)
}
}
impl<'a, T: AutoLaminate<U> + ByteLamination<'a> + 'a, U> AutoLaminate<U> for Zstd<'a, T> {
fn laminate(item: U) -> Result<Self, Box<dyn Error>> {
let lamination = T::laminate(item)?;
let bytes: Cow<'_, [u8]> = lamination.as_cow_bytes();
Zstd::compress(&bytes, DEFAULT_ZSTD_LEVEL)
}
}
impl<'a, T: AutoDelaminate<U> + ByteLamination<'a>, U> AutoDelaminate<U> for Zstd<'a, T> {
fn delaminate(self) -> Result<U, Box<dyn Error>> {
let memory_limit = max(
DEFAULT_MEMORY_LIMIT_MINIMUM,
DEFAULT_MEMORY_LIMIT_MULTIPLIER * self.bytes.len(),
);
let bytes = self.decompress(memory_limit)?;
T::try_from_bytes(Cow::Owned(bytes))?.delaminate()
}
}