1use std::str::FromStr;
10
11use bytes::Bytes;
12use serde::{Deserialize, Serialize};
13use thiserror::Error;
14
15pub mod cpu_zstd;
16pub mod dietgpu;
17pub mod dispatcher;
18#[cfg(feature = "nvcomp-gpu")]
19mod ferro_compress;
20pub mod index;
21pub mod multipart;
22pub mod nvcomp;
23pub mod passthrough;
24pub mod registry;
25
26pub use registry::CodecRegistry;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
30#[serde(rename_all = "kebab-case")]
31pub enum CodecKind {
32 Passthrough,
33 NvcompBitcomp,
34 NvcompGans,
35 NvcompZstd,
36 DietGpuAns,
37 CpuZstd,
38 NvcompGDeflate,
43}
44
45impl CodecKind {
46 pub fn as_str(self) -> &'static str {
47 match self {
48 Self::Passthrough => "passthrough",
49 Self::NvcompBitcomp => "nvcomp-bitcomp",
50 Self::NvcompGans => "nvcomp-gans",
51 Self::NvcompZstd => "nvcomp-zstd",
52 Self::DietGpuAns => "dietgpu-ans",
53 Self::CpuZstd => "cpu-zstd",
54 Self::NvcompGDeflate => "nvcomp-gdeflate",
55 }
56 }
57
58 pub fn id(self) -> u32 {
61 match self {
62 Self::Passthrough => 0,
63 Self::CpuZstd => 1,
64 Self::NvcompZstd => 2,
65 Self::NvcompBitcomp => 3,
66 Self::NvcompGans => 4,
67 Self::DietGpuAns => 5,
68 Self::NvcompGDeflate => 6,
69 }
70 }
71
72 pub fn from_id(id: u32) -> Option<Self> {
73 Some(match id {
74 0 => Self::Passthrough,
75 1 => Self::CpuZstd,
76 2 => Self::NvcompZstd,
77 3 => Self::NvcompBitcomp,
78 4 => Self::NvcompGans,
79 5 => Self::DietGpuAns,
80 6 => Self::NvcompGDeflate,
81 _ => return None,
82 })
83 }
84}
85
86#[derive(Debug, thiserror::Error)]
87#[error("unknown codec kind: {0}")]
88pub struct ParseCodecKindError(String);
89
90impl FromStr for CodecKind {
91 type Err = ParseCodecKindError;
92 fn from_str(s: &str) -> Result<Self, Self::Err> {
93 Ok(match s {
94 "passthrough" => Self::Passthrough,
95 "nvcomp-bitcomp" => Self::NvcompBitcomp,
96 "nvcomp-gans" => Self::NvcompGans,
97 "nvcomp-zstd" => Self::NvcompZstd,
98 "dietgpu-ans" => Self::DietGpuAns,
99 "cpu-zstd" => Self::CpuZstd,
100 "nvcomp-gdeflate" => Self::NvcompGDeflate,
101 other => return Err(ParseCodecKindError(other.into())),
102 })
103 }
104}
105
106#[derive(Debug, Clone, Serialize, Deserialize)]
108pub struct ChunkManifest {
109 pub codec: CodecKind,
110 pub original_size: u64,
111 pub compressed_size: u64,
112 pub crc32c: u32,
113}
114
115#[derive(Debug, Error)]
118pub enum CodecError {
119 #[error("codec mismatch: expected {expected:?}, got {got:?}")]
120 CodecMismatch { expected: CodecKind, got: CodecKind },
121
122 #[error("crc32c mismatch (chunk corruption?): expected {expected:#010x}, got {got:#010x}")]
123 CrcMismatch { expected: u32, got: u32 },
124
125 #[error("compressed size mismatch: manifest says {expected} bytes, payload is {got} bytes")]
126 SizeMismatch { expected: u64, got: u64 },
127
128 #[error("compression backend error: {0}")]
129 Backend(#[from] anyhow::Error),
130
131 #[error("io error: {0}")]
132 Io(#[from] std::io::Error),
133
134 #[error("blocking-task join error: {0}")]
135 Join(#[from] tokio::task::JoinError),
136
137 #[error("codec {0:?} is not registered in this CodecRegistry")]
138 UnregisteredCodec(CodecKind),
139}
140
141#[async_trait::async_trait]
146pub trait Codec: Send + Sync {
147 fn kind(&self) -> CodecKind;
149
150 async fn compress(&self, input: Bytes) -> Result<(Bytes, ChunkManifest), CodecError>;
152
153 async fn decompress(&self, input: Bytes, manifest: &ChunkManifest)
155 -> Result<Bytes, CodecError>;
156}
157
158pub use dispatcher::CodecDispatcher;