use derive_more::{Display, From};
use serde::{Deserialize, Deserializer, Serialize};
use zarrs_metadata::ConfigurationSerialize;
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Display, From)]
#[non_exhaustive]
#[serde(untagged)]
pub enum PcodecCodecConfiguration {
V1(PcodecCodecConfigurationV1),
}
impl ConfigurationSerialize for PcodecCodecConfiguration {}
impl Default for PcodecCodecConfiguration {
fn default() -> Self {
Self::V1(PcodecCodecConfigurationV1::default())
}
}
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug, Display)]
#[display("{}", serde_json::to_string(self).unwrap_or_default())]
#[serde(default)] #[serde(deny_unknown_fields)]
pub struct PcodecCodecConfigurationV1 {
pub level: PcodecCompressionLevel,
pub mode_spec: PcodecModeSpecConfiguration,
pub delta_spec: PcodecDeltaSpecConfiguration,
pub paging_spec: PcodecPagingSpecConfiguration,
pub delta_encoding_order: Option<PcodecDeltaEncodingOrder>,
pub equal_pages_up_to: usize,
}
impl Default for PcodecCodecConfigurationV1 {
fn default() -> Self {
Self {
level: PcodecCompressionLevel::default(),
mode_spec: PcodecModeSpecConfiguration::default(),
delta_spec: PcodecDeltaSpecConfiguration::default(),
paging_spec: PcodecPagingSpecConfiguration::default(),
delta_encoding_order: None,
equal_pages_up_to: default_equal_pages_up_to(),
}
}
}
#[derive(Serialize, Deserialize, Default, Clone, Copy, PartialEq, Debug, Display)]
#[serde(rename_all = "snake_case")]
pub enum PcodecModeSpecConfiguration {
#[default]
Auto,
Classic,
}
#[derive(Serialize, Deserialize, Default, Debug, Clone, Copy, PartialEq, Display)]
#[serde(rename_all = "snake_case")]
pub enum PcodecDeltaSpecConfiguration {
#[default]
Auto,
None,
TryConsecutive,
TryLookback,
}
#[derive(Serialize, Deserialize, Default, Debug, Clone, Copy, PartialEq, Display)]
#[serde(rename_all = "snake_case")]
pub enum PcodecPagingSpecConfiguration {
#[default]
EqualPagesUpTo,
}
#[derive(Serialize, Copy, Clone, Debug, Eq, PartialEq)]
pub struct PcodecCompressionLevel(u8);
impl Default for PcodecCompressionLevel {
fn default() -> Self {
Self(8)
}
}
macro_rules! pcodec_compression_level_try_from {
( $t:ty ) => {
impl TryFrom<$t> for PcodecCompressionLevel {
type Error = $t;
fn try_from(level: $t) -> Result<Self, Self::Error> {
if level <= 12 {
Ok(Self(unsafe { u8::try_from(level).unwrap_unchecked() }))
} else {
Err(level)
}
}
}
};
}
pcodec_compression_level_try_from!(u8);
pcodec_compression_level_try_from!(u16);
pcodec_compression_level_try_from!(u32);
pcodec_compression_level_try_from!(u64);
pcodec_compression_level_try_from!(usize);
impl<'de> Deserialize<'de> for PcodecCompressionLevel {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let level = u8::deserialize(d)?;
if level <= 12 {
Ok(Self(level))
} else {
Err(serde::de::Error::custom(
"pcodec compression level must be between 0 and 12",
))
}
}
}
impl PcodecCompressionLevel {
pub fn new<N: num::Unsigned + std::cmp::PartialOrd<u8>>(compression_level: N) -> Result<Self, N>
where
u8: TryFrom<N>,
{
if compression_level <= 12 {
Ok(Self(unsafe {
u8::try_from(compression_level).unwrap_unchecked()
}))
} else {
Err(compression_level)
}
}
#[must_use]
pub const fn as_usize(&self) -> usize {
self.0 as usize
}
}
#[derive(Serialize, Copy, Clone, Debug, Eq, PartialEq)]
pub struct PcodecDeltaEncodingOrder(u8);
macro_rules! pcodec_delta_encoding_order_level_try_from {
( $t:ty ) => {
impl TryFrom<$t> for PcodecDeltaEncodingOrder {
type Error = $t;
fn try_from(level: $t) -> Result<Self, Self::Error> {
if level <= 7 {
Ok(Self(unsafe { u8::try_from(level).unwrap_unchecked() }))
} else {
Err(level)
}
}
}
};
}
pcodec_delta_encoding_order_level_try_from!(u8);
pcodec_delta_encoding_order_level_try_from!(u16);
pcodec_delta_encoding_order_level_try_from!(u32);
pcodec_delta_encoding_order_level_try_from!(u64);
pcodec_delta_encoding_order_level_try_from!(usize);
impl<'de> Deserialize<'de> for PcodecDeltaEncodingOrder {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let level = u8::deserialize(d)?;
if level <= 7 {
Ok(Self(level))
} else {
Err(serde::de::Error::custom(
"pcodec compression level must be between 0 and 7",
))
}
}
}
impl PcodecDeltaEncodingOrder {
pub fn new<N: num::Unsigned + std::cmp::PartialOrd<u8>>(
delta_encoding_order: N,
) -> Result<Self, N>
where
u8: TryFrom<N>,
{
if delta_encoding_order <= 7 {
Ok(Self(unsafe {
u8::try_from(delta_encoding_order).unwrap_unchecked()
}))
} else {
Err(delta_encoding_order)
}
}
#[must_use]
pub const fn as_usize(&self) -> usize {
self.0 as usize
}
}
const fn default_equal_pages_up_to() -> usize {
1 << 18
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn codec_pcodec_valid_empty() {
serde_json::from_str::<PcodecCodecConfiguration>(
r"{
}",
)
.unwrap();
}
#[test]
fn codec_pcodec_valid_auto() {
serde_json::from_str::<PcodecCodecConfiguration>(
r#"{
"level": 8,
"delta_encoding_order": 2,
"mode_spec": "auto",
"equal_pages_up_to": 262144
}"#,
)
.unwrap();
}
#[test]
fn codec_pcodec_valid_classic() {
serde_json::from_str::<PcodecCodecConfiguration>(
r#"{
"level": 8,
"delta_encoding_order": 2,
"mode_spec": "classic",
"equal_pages_up_to": 262144
}"#,
)
.unwrap();
}
#[test]
fn codec_pcodec_invalid_level() {
assert!(serde_json::from_str::<PcodecCodecConfiguration>(
r#"{
"level": 13,
"delta_encoding_order": 2,
"mode_spec": "auto",
"equal_pages_up_to": 262144
}"#,
)
.is_err());
}
#[test]
fn codec_pcodec_invalid_delta_encoding_order() {
assert!(serde_json::from_str::<PcodecCodecConfiguration>(
r#"{
"level": 8,
"delta_encoding_order": 8,
"mode_spec": "auto",
"equal_pages_up_to": 262144
}"#,
)
.is_err());
}
}