dsi_bitstream/dispatch/
codes.rs

1/*
2 * SPDX-FileCopyrightText: 2025 Tommaso Fontana
3 * SPDX-FileCopyrightText: 2025 Inria
4 * SPDX-FileCopyrightText: 2025 Sebastiano Vigna
5 *
6 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
7 */
8
9//! Enumeration of all available codes, with associated read and write methods.
10//!
11//! This is the slower and more generic form of dispatching, mostly used for
12//! testing and writing examples. For faster dispatching, consider using
13//! [dynamic] or [static] dispatch.
14
15use super::*;
16#[cfg(feature = "mem_dbg")]
17use mem_dbg::{MemDbg, MemSize};
18
19#[derive(Debug, Clone, Copy, Eq)]
20#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
21#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
22#[non_exhaustive]
23/// An enum whose variants represent all the available codes.
24///
25/// This enum is kept in sync with implementations in the
26/// [`codes`](crate::codes) module.
27///
28/// Both [`Display`](std::fmt::Display) and [`FromStr`](std::str::FromStr) are
29/// implemented for this enum in a dual way, which makes it possible to store a
30/// code as a string in a configuration file, and then parse it back.
31pub enum Codes {
32    Unary,
33    Gamma,
34    Delta,
35    Omega,
36    VByteLe,
37    VByteBe,
38    Zeta { k: usize },
39    Pi { k: usize },
40    Golomb { b: usize },
41    ExpGolomb { k: usize },
42    Rice { log2_b: usize },
43}
44
45/// Some codes are equivalent, so we implement [`PartialEq`] to make them
46/// interchangeable so `Codes::Unary == Codes::Rice{log2_b: 0}`.
47impl PartialEq for Codes {
48    fn eq(&self, other: &Self) -> bool {
49        match (self, other) {
50            // First we check the equivalence classes
51            (
52                Self::Unary | Self::Rice { log2_b: 0 } | Self::Golomb { b: 1 },
53                Self::Unary | Self::Rice { log2_b: 0 } | Self::Golomb { b: 1 },
54            ) => true,
55            (
56                Self::Gamma | Self::Zeta { k: 1 } | Self::ExpGolomb { k: 0 },
57                Self::Gamma | Self::Zeta { k: 1 } | Self::ExpGolomb { k: 0 },
58            ) => true,
59            (
60                Self::Golomb { b: 2 } | Self::Rice { log2_b: 1 },
61                Self::Golomb { b: 2 } | Self::Rice { log2_b: 1 },
62            ) => true,
63            (
64                Self::Golomb { b: 4 } | Self::Rice { log2_b: 2 },
65                Self::Golomb { b: 4 } | Self::Rice { log2_b: 2 },
66            ) => true,
67            (
68                Self::Golomb { b: 8 } | Self::Rice { log2_b: 3 },
69                Self::Golomb { b: 8 } | Self::Rice { log2_b: 3 },
70            ) => true,
71            // we know that we are not in a special case, so we can directly
72            // compare them naively
73            (Self::Delta, Self::Delta) => true,
74            (Self::Omega, Self::Omega) => true,
75            (Self::VByteLe, Self::VByteLe) => true,
76            (Self::VByteBe, Self::VByteBe) => true,
77            (Self::Zeta { k }, Self::Zeta { k: k2 }) => k == k2,
78            (Self::Pi { k }, Self::Pi { k: k2 }) => k == k2,
79            (Self::Golomb { b }, Self::Golomb { b: b2 }) => b == b2,
80            (Self::ExpGolomb { k }, Self::ExpGolomb { k: k2 }) => k == k2,
81            (Self::Rice { log2_b }, Self::Rice { log2_b: log2_b2 }) => log2_b == log2_b2,
82            _ => false,
83        }
84    }
85}
86
87impl Codes {
88    /// Delegate to the [`DynamicCodeRead`] implementation.
89    ///
90    /// This inherent method is provided to reduce ambiguity in method
91    /// resolution.
92    #[inline(always)]
93    pub fn read<E: Endianness, CR: CodesRead<E> + ?Sized>(
94        &self,
95        reader: &mut CR,
96    ) -> Result<u64, CR::Error> {
97        DynamicCodeRead::read(self, reader)
98    }
99
100    /// Delegate to the [`DynamicCodeWrite`] implementation.
101    ///
102    /// This inherent method is provided to reduce ambiguity in method
103    /// resolution.
104    #[inline(always)]
105    pub fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
106        &self,
107        writer: &mut CW,
108        value: u64,
109    ) -> Result<usize, CW::Error> {
110        DynamicCodeWrite::write(self, writer, value)
111    }
112
113    /// Convert a code to the constant enum [`code_consts`] used for [`ConstCode`].
114    /// This is mostly used to verify that the code is supported by
115    /// [`ConstCode`].
116    pub fn to_code_const(&self) -> Result<usize> {
117        Ok(match self {
118            Self::Unary => code_consts::UNARY,
119            Self::Gamma => code_consts::GAMMA,
120            Self::Delta => code_consts::DELTA,
121            Self::Omega => code_consts::OMEGA,
122            Self::VByteLe => code_consts::VBYTE_LE,
123            Self::VByteBe => code_consts::VBYTE_BE,
124            Self::Zeta { k: 1 } => code_consts::ZETA1,
125            Self::Zeta { k: 2 } => code_consts::ZETA2,
126            Self::Zeta { k: 3 } => code_consts::ZETA3,
127            Self::Zeta { k: 4 } => code_consts::ZETA4,
128            Self::Zeta { k: 5 } => code_consts::ZETA5,
129            Self::Zeta { k: 6 } => code_consts::ZETA6,
130            Self::Zeta { k: 7 } => code_consts::ZETA7,
131            Self::Zeta { k: 8 } => code_consts::ZETA8,
132            Self::Zeta { k: 9 } => code_consts::ZETA9,
133            Self::Zeta { k: 10 } => code_consts::ZETA10,
134            Self::Rice { log2_b: 0 } => code_consts::RICE0,
135            Self::Rice { log2_b: 1 } => code_consts::RICE1,
136            Self::Rice { log2_b: 2 } => code_consts::RICE2,
137            Self::Rice { log2_b: 3 } => code_consts::RICE3,
138            Self::Rice { log2_b: 4 } => code_consts::RICE4,
139            Self::Rice { log2_b: 5 } => code_consts::RICE5,
140            Self::Rice { log2_b: 6 } => code_consts::RICE6,
141            Self::Rice { log2_b: 7 } => code_consts::RICE7,
142            Self::Rice { log2_b: 8 } => code_consts::RICE8,
143            Self::Rice { log2_b: 9 } => code_consts::RICE9,
144            Self::Rice { log2_b: 10 } => code_consts::RICE10,
145            Self::Pi { k: 0 } => code_consts::PI0,
146            Self::Pi { k: 1 } => code_consts::PI1,
147            Self::Pi { k: 2 } => code_consts::PI2,
148            Self::Pi { k: 3 } => code_consts::PI3,
149            Self::Pi { k: 4 } => code_consts::PI4,
150            Self::Pi { k: 5 } => code_consts::PI5,
151            Self::Pi { k: 6 } => code_consts::PI6,
152            Self::Pi { k: 7 } => code_consts::PI7,
153            Self::Pi { k: 8 } => code_consts::PI8,
154            Self::Pi { k: 9 } => code_consts::PI9,
155            Self::Pi { k: 10 } => code_consts::PI10,
156            Self::Golomb { b: 1 } => code_consts::GOLOMB1,
157            Self::Golomb { b: 2 } => code_consts::GOLOMB2,
158            Self::Golomb { b: 3 } => code_consts::GOLOMB3,
159            Self::Golomb { b: 4 } => code_consts::GOLOMB4,
160            Self::Golomb { b: 5 } => code_consts::GOLOMB5,
161            Self::Golomb { b: 6 } => code_consts::GOLOMB6,
162            Self::Golomb { b: 7 } => code_consts::GOLOMB7,
163            Self::Golomb { b: 8 } => code_consts::GOLOMB8,
164            Self::Golomb { b: 9 } => code_consts::GOLOMB9,
165            Self::Golomb { b: 10 } => code_consts::GOLOMB10,
166            Self::ExpGolomb { k: 0 } => code_consts::EXP_GOLOMB0,
167            Self::ExpGolomb { k: 1 } => code_consts::EXP_GOLOMB1,
168            Self::ExpGolomb { k: 2 } => code_consts::EXP_GOLOMB2,
169            Self::ExpGolomb { k: 3 } => code_consts::EXP_GOLOMB3,
170            Self::ExpGolomb { k: 4 } => code_consts::EXP_GOLOMB4,
171            Self::ExpGolomb { k: 5 } => code_consts::EXP_GOLOMB5,
172            Self::ExpGolomb { k: 6 } => code_consts::EXP_GOLOMB6,
173            Self::ExpGolomb { k: 7 } => code_consts::EXP_GOLOMB7,
174            Self::ExpGolomb { k: 8 } => code_consts::EXP_GOLOMB8,
175            Self::ExpGolomb { k: 9 } => code_consts::EXP_GOLOMB9,
176            Self::ExpGolomb { k: 10 } => code_consts::EXP_GOLOMB10,
177            _ => {
178                return Err(anyhow::anyhow!(
179                    "Code {:?} not supported as const code",
180                    self
181                ))
182            }
183        })
184    }
185
186    /// Convert a value from [`code_consts`] to a code.
187    pub fn from_code_const(const_code: usize) -> Result<Self> {
188        Ok(match const_code {
189            code_consts::UNARY => Self::Unary,
190            code_consts::GAMMA => Self::Gamma,
191            code_consts::DELTA => Self::Delta,
192            code_consts::OMEGA => Self::Omega,
193            code_consts::VBYTE_LE => Self::VByteLe,
194            code_consts::VBYTE_BE => Self::VByteBe,
195            code_consts::ZETA2 => Self::Zeta { k: 2 },
196            code_consts::ZETA3 => Self::Zeta { k: 3 },
197            code_consts::ZETA4 => Self::Zeta { k: 4 },
198            code_consts::ZETA5 => Self::Zeta { k: 5 },
199            code_consts::ZETA6 => Self::Zeta { k: 6 },
200            code_consts::ZETA7 => Self::Zeta { k: 7 },
201            code_consts::ZETA8 => Self::Zeta { k: 8 },
202            code_consts::ZETA9 => Self::Zeta { k: 9 },
203            code_consts::ZETA10 => Self::Zeta { k: 10 },
204            code_consts::RICE1 => Self::Rice { log2_b: 1 },
205            code_consts::RICE2 => Self::Rice { log2_b: 2 },
206            code_consts::RICE3 => Self::Rice { log2_b: 3 },
207            code_consts::RICE4 => Self::Rice { log2_b: 4 },
208            code_consts::RICE5 => Self::Rice { log2_b: 5 },
209            code_consts::RICE6 => Self::Rice { log2_b: 6 },
210            code_consts::RICE7 => Self::Rice { log2_b: 7 },
211            code_consts::RICE8 => Self::Rice { log2_b: 8 },
212            code_consts::RICE9 => Self::Rice { log2_b: 9 },
213            code_consts::RICE10 => Self::Rice { log2_b: 10 },
214            code_consts::PI1 => Self::Pi { k: 1 },
215            code_consts::PI2 => Self::Pi { k: 2 },
216            code_consts::PI3 => Self::Pi { k: 3 },
217            code_consts::PI4 => Self::Pi { k: 4 },
218            code_consts::PI5 => Self::Pi { k: 5 },
219            code_consts::PI6 => Self::Pi { k: 6 },
220            code_consts::PI7 => Self::Pi { k: 7 },
221            code_consts::PI8 => Self::Pi { k: 8 },
222            code_consts::PI9 => Self::Pi { k: 9 },
223            code_consts::PI10 => Self::Pi { k: 10 },
224            code_consts::GOLOMB3 => Self::Golomb { b: 3 },
225            code_consts::GOLOMB5 => Self::Golomb { b: 5 },
226            code_consts::GOLOMB6 => Self::Golomb { b: 6 },
227            code_consts::GOLOMB7 => Self::Golomb { b: 7 },
228            code_consts::GOLOMB9 => Self::Golomb { b: 9 },
229            code_consts::GOLOMB10 => Self::Golomb { b: 10 },
230            code_consts::EXP_GOLOMB1 => Self::ExpGolomb { k: 1 },
231            code_consts::EXP_GOLOMB2 => Self::ExpGolomb { k: 2 },
232            code_consts::EXP_GOLOMB3 => Self::ExpGolomb { k: 3 },
233            code_consts::EXP_GOLOMB4 => Self::ExpGolomb { k: 4 },
234            code_consts::EXP_GOLOMB5 => Self::ExpGolomb { k: 5 },
235            code_consts::EXP_GOLOMB6 => Self::ExpGolomb { k: 6 },
236            code_consts::EXP_GOLOMB7 => Self::ExpGolomb { k: 7 },
237            code_consts::EXP_GOLOMB8 => Self::ExpGolomb { k: 8 },
238            code_consts::EXP_GOLOMB9 => Self::ExpGolomb { k: 9 },
239            code_consts::EXP_GOLOMB10 => Self::ExpGolomb { k: 10 },
240            _ => return Err(anyhow::anyhow!("Code {} not supported", const_code)),
241        })
242    }
243}
244
245impl DynamicCodeRead for Codes {
246    #[inline]
247    fn read<E: Endianness, CR: CodesRead<E> + ?Sized>(
248        &self,
249        reader: &mut CR,
250    ) -> Result<u64, CR::Error> {
251        Ok(match self {
252            Codes::Unary => reader.read_unary()?,
253            Codes::Gamma => reader.read_gamma()?,
254            Codes::Delta => reader.read_delta()?,
255            Codes::Omega => reader.read_omega()?,
256            Codes::VByteBe => reader.read_vbyte_be()?,
257            Codes::VByteLe => reader.read_vbyte_le()?,
258            Codes::Zeta { k: 3 } => reader.read_zeta3()?,
259            Codes::Zeta { k } => reader.read_zeta(*k)?,
260            Codes::Pi { k } => reader.read_pi(*k)?,
261            Codes::Golomb { b } => reader.read_golomb(*b as u64)?,
262            Codes::ExpGolomb { k } => reader.read_exp_golomb(*k)?,
263            Codes::Rice { log2_b } => reader.read_rice(*log2_b)?,
264        })
265    }
266}
267
268impl DynamicCodeWrite for Codes {
269    #[inline]
270    fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
271        &self,
272        writer: &mut CW,
273        value: u64,
274    ) -> Result<usize, CW::Error> {
275        Ok(match self {
276            Codes::Unary => writer.write_unary(value)?,
277            Codes::Gamma => writer.write_gamma(value)?,
278            Codes::Delta => writer.write_delta(value)?,
279            Codes::Omega => writer.write_omega(value)?,
280            Codes::VByteBe => writer.write_vbyte_be(value)?,
281            Codes::VByteLe => writer.write_vbyte_le(value)?,
282            Codes::Zeta { k: 1 } => writer.write_gamma(value)?,
283            Codes::Zeta { k: 3 } => writer.write_zeta3(value)?,
284            Codes::Zeta { k } => writer.write_zeta(value, *k)?,
285            Codes::Pi { k } => writer.write_pi(value, *k)?,
286            Codes::Golomb { b } => writer.write_golomb(value, *b as u64)?,
287            Codes::ExpGolomb { k } => writer.write_exp_golomb(value, *k)?,
288            Codes::Rice { log2_b } => writer.write_rice(value, *log2_b)?,
289        })
290    }
291}
292
293impl<E: Endianness, CR: CodesRead<E> + ?Sized> StaticCodeRead<E, CR> for Codes {
294    #[inline(always)]
295    fn read(&self, reader: &mut CR) -> Result<u64, CR::Error> {
296        <Self as DynamicCodeRead>::read(self, reader)
297    }
298}
299
300impl<E: Endianness, CW: CodesWrite<E> + ?Sized> StaticCodeWrite<E, CW> for Codes {
301    #[inline(always)]
302    fn write(&self, writer: &mut CW, value: u64) -> Result<usize, CW::Error> {
303        <Self as DynamicCodeWrite>::write(self, writer, value)
304    }
305}
306
307impl CodeLen for Codes {
308    #[inline]
309    fn len(&self, value: u64) -> usize {
310        match self {
311            Codes::Unary => value as usize + 1,
312            Codes::Gamma => len_gamma(value),
313            Codes::Delta => len_delta(value),
314            Codes::Omega => len_omega(value),
315            Codes::VByteLe | Codes::VByteBe => bit_len_vbyte(value),
316            Codes::Zeta { k: 1 } => len_gamma(value),
317            Codes::Zeta { k } => len_zeta(value, *k),
318            Codes::Pi { k } => len_pi(value, *k),
319            Codes::Golomb { b } => len_golomb(value, *b as u64),
320            Codes::ExpGolomb { k } => len_exp_golomb(value, *k),
321            Codes::Rice { log2_b } => len_rice(value, *log2_b),
322        }
323    }
324}
325
326#[derive(Debug)]
327/// Error type for parsing a code from a string.
328pub enum CodeError {
329    ParseError(core::num::ParseIntError),
330    UnknownCode(String),
331}
332impl std::error::Error for CodeError {}
333impl core::fmt::Display for CodeError {
334    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
335        match self {
336            CodeError::ParseError(e) => write!(f, "Parse error: {}", e),
337            CodeError::UnknownCode(s) => write!(f, "Unknown code: {}", s),
338        }
339    }
340}
341
342impl From<core::num::ParseIntError> for CodeError {
343    fn from(e: core::num::ParseIntError) -> Self {
344        CodeError::ParseError(e)
345    }
346}
347
348impl core::fmt::Display for Codes {
349    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
350        match self {
351            Codes::Unary => write!(f, "Unary"),
352            Codes::Gamma => write!(f, "Gamma"),
353            Codes::Delta => write!(f, "Delta"),
354            Codes::Omega => write!(f, "Omega"),
355            Codes::VByteBe => write!(f, "VByteBe"),
356            Codes::VByteLe => write!(f, "VByteLe"),
357            Codes::Zeta { k } => write!(f, "Zeta({})", k),
358            Codes::Pi { k } => write!(f, "Pi({})", k),
359            Codes::Golomb { b } => write!(f, "Golomb({})", b),
360            Codes::ExpGolomb { k } => write!(f, "ExpGolomb({})", k),
361            Codes::Rice { log2_b } => write!(f, "Rice({})", log2_b),
362        }
363    }
364}
365
366impl std::str::FromStr for Codes {
367    type Err = CodeError;
368
369    fn from_str(s: &str) -> Result<Self, Self::Err> {
370        match s {
371            "Unary" => Ok(Codes::Unary),
372            "Gamma" => Ok(Codes::Gamma),
373            "Delta" => Ok(Codes::Delta),
374            "Omega" => Ok(Codes::Omega),
375            "VByteBe" => Ok(Codes::VByteBe),
376
377            _ => {
378                let mut parts = s.split('(');
379                let name = parts
380                    .next()
381                    .ok_or_else(|| CodeError::UnknownCode(format!("Could not parse {}", s)))?;
382                let k = parts
383                    .next()
384                    .ok_or_else(|| CodeError::UnknownCode(format!("Could not parse {}", s)))?
385                    .split(')')
386                    .next()
387                    .ok_or_else(|| CodeError::UnknownCode(format!("Could not parse {}", s)))?;
388                match name {
389                    "Zeta" => Ok(Codes::Zeta { k: k.parse()? }),
390                    "Pi" => Ok(Codes::Pi { k: k.parse()? }),
391                    "Golomb" => Ok(Codes::Golomb { b: k.parse()? }),
392                    "ExpGolomb" => Ok(Codes::ExpGolomb { k: k.parse()? }),
393                    "Rice" => Ok(Codes::Rice { log2_b: k.parse()? }),
394                    _ => Err(CodeError::UnknownCode(format!("Could not parse {}", name))),
395                }
396            }
397        }
398    }
399}