Skip to main content

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 = "serde")]
17use alloc::string::{String, ToString};
18#[cfg(feature = "mem_dbg")]
19use mem_dbg::{MemDbg, MemSize};
20
21/// An enum whose variants represent all the available codes.
22///
23/// This enum is kept in sync with implementations in the
24/// [`codes`](crate::codes) module.
25///
26/// Both [`Display`](core::fmt::Display) and [`FromStr`](core::str::FromStr) are
27/// implemented for this enum in a compatible way, making it possible to store a
28/// code as a string in a configuration file and then parse it back.
29#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
30#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
31#[cfg_attr(feature = "mem_dbg", mem_size_flat)]
32#[cfg_attr(feature = "fuzz", derive(arbitrary::Arbitrary))]
33#[non_exhaustive]
34pub enum Codes {
35    Unary,
36    Gamma,
37    Delta,
38    Omega,
39    VByteLe,
40    VByteBe,
41    Zeta(usize),
42    Pi(usize),
43    Golomb(u64),
44    ExpGolomb(usize),
45    Rice(usize),
46}
47
48impl Codes {
49    /// Returns the canonical form of this code.
50    ///
51    /// Some codes are equivalent, in the sense that they are defined
52    /// differently, but they give rise to the same codewords. Among equivalent
53    /// codes, there is usually one that is faster to encode and decode, which
54    /// we call the _canonical representative_ of the equivalence class.
55    ///
56    /// The mapping is:
57    ///
58    /// - [`Rice(0)`](Codes::Rice),
59    ///   [`Golomb(1)`](Codes::Golomb) →
60    ///   [`Unary`](Codes::Unary)
61    ///
62    /// - [`Zeta(1)`](Codes::Zeta),
63    ///   [`ExpGolomb(0)`](Codes::ExpGolomb),
64    ///   [`Pi(0)`](Codes::Pi) →
65    ///   [`Gamma`](Codes::Gamma)
66    ///
67    /// - [`Golomb(2ⁿ)`](Codes::Golomb) → [`Rice(n)`](Codes::Rice)
68    #[must_use]
69    pub const fn canonicalize(self) -> Self {
70        match self {
71            Self::Zeta(1) | Self::ExpGolomb(0) | Self::Pi(0) => Self::Gamma,
72            Self::Rice(0) | Self::Golomb(1) => Self::Unary,
73            Self::Golomb(b) if b.is_power_of_two() => Self::Rice(b.trailing_zeros() as usize),
74            other => other,
75        }
76    }
77
78    /// Delegates to the [`DynamicCodeRead`] implementation.
79    ///
80    /// This inherent method is provided to reduce ambiguity in method
81    /// resolution.
82    #[inline(always)]
83    pub fn read<E: Endianness, CR: CodesRead<E> + ?Sized>(
84        &self,
85        reader: &mut CR,
86    ) -> Result<u64, CR::Error> {
87        DynamicCodeRead::read(self, reader)
88    }
89
90    /// Delegates to the [`DynamicCodeWrite`] implementation.
91    ///
92    /// This inherent method is provided to reduce ambiguity in method
93    /// resolution.
94    #[inline(always)]
95    pub fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
96        &self,
97        writer: &mut CW,
98        n: u64,
99    ) -> Result<usize, CW::Error> {
100        DynamicCodeWrite::write(self, writer, n)
101    }
102
103    /// Converts a code to the constants in the [`code_consts`] module used for
104    /// [`ConstCode`]. This is mostly used to verify that the code is supported
105    /// by [`ConstCode`].
106    ///
107    /// The code is [canonicalized](Codes::canonicalize) before
108    /// the conversion, so equivalent codes map to the same
109    /// constant.
110    ///
111    /// # Errors
112    ///
113    /// Returns [`DispatchError::UnsupportedCode`] if the (canonicalized)
114    /// code has no corresponding constant in [`code_consts`].
115    pub const fn to_code_const(&self) -> Result<usize, DispatchError> {
116        Ok(match self.canonicalize() {
117            Self::Unary => code_consts::UNARY,
118            Self::Gamma => code_consts::GAMMA,
119            Self::Delta => code_consts::DELTA,
120            Self::Omega => code_consts::OMEGA,
121            Self::VByteLe => code_consts::VBYTE_LE,
122            Self::VByteBe => code_consts::VBYTE_BE,
123            Self::Zeta(2) => code_consts::ZETA2,
124            Self::Zeta(3) => code_consts::ZETA3,
125            Self::Zeta(4) => code_consts::ZETA4,
126            Self::Zeta(5) => code_consts::ZETA5,
127            Self::Zeta(6) => code_consts::ZETA6,
128            Self::Zeta(7) => code_consts::ZETA7,
129            Self::Zeta(8) => code_consts::ZETA8,
130            Self::Zeta(9) => code_consts::ZETA9,
131            Self::Zeta(10) => code_consts::ZETA10,
132            Self::Rice(1) => code_consts::RICE1,
133            Self::Rice(2) => code_consts::RICE2,
134            Self::Rice(3) => code_consts::RICE3,
135            Self::Rice(4) => code_consts::RICE4,
136            Self::Rice(5) => code_consts::RICE5,
137            Self::Rice(6) => code_consts::RICE6,
138            Self::Rice(7) => code_consts::RICE7,
139            Self::Rice(8) => code_consts::RICE8,
140            Self::Rice(9) => code_consts::RICE9,
141            Self::Rice(10) => code_consts::RICE10,
142            Self::Pi(1) => code_consts::PI1,
143            Self::Pi(2) => code_consts::PI2,
144            Self::Pi(3) => code_consts::PI3,
145            Self::Pi(4) => code_consts::PI4,
146            Self::Pi(5) => code_consts::PI5,
147            Self::Pi(6) => code_consts::PI6,
148            Self::Pi(7) => code_consts::PI7,
149            Self::Pi(8) => code_consts::PI8,
150            Self::Pi(9) => code_consts::PI9,
151            Self::Pi(10) => code_consts::PI10,
152            Self::Golomb(3) => code_consts::GOLOMB3,
153            Self::Golomb(5) => code_consts::GOLOMB5,
154            Self::Golomb(6) => code_consts::GOLOMB6,
155            Self::Golomb(7) => code_consts::GOLOMB7,
156            Self::Golomb(9) => code_consts::GOLOMB9,
157            Self::Golomb(10) => code_consts::GOLOMB10,
158            Self::ExpGolomb(1) => code_consts::EXP_GOLOMB1,
159            Self::ExpGolomb(2) => code_consts::EXP_GOLOMB2,
160            Self::ExpGolomb(3) => code_consts::EXP_GOLOMB3,
161            Self::ExpGolomb(4) => code_consts::EXP_GOLOMB4,
162            Self::ExpGolomb(5) => code_consts::EXP_GOLOMB5,
163            Self::ExpGolomb(6) => code_consts::EXP_GOLOMB6,
164            Self::ExpGolomb(7) => code_consts::EXP_GOLOMB7,
165            Self::ExpGolomb(8) => code_consts::EXP_GOLOMB8,
166            Self::ExpGolomb(9) => code_consts::EXP_GOLOMB9,
167            Self::ExpGolomb(10) => code_consts::EXP_GOLOMB10,
168            _ => {
169                return Err(DispatchError::UnsupportedCode(*self));
170            }
171        })
172    }
173
174    /// Converts a value from [`code_consts`] to a code.
175    ///
176    /// # Errors
177    ///
178    /// Returns [`DispatchError::UnsupportedCodeConst`] if the value
179    /// does not correspond to any known code constant.
180    pub const fn from_code_const(const_code: usize) -> Result<Self, DispatchError> {
181        Ok(match const_code {
182            code_consts::UNARY => Self::Unary,
183            code_consts::GAMMA => Self::Gamma,
184            code_consts::DELTA => Self::Delta,
185            code_consts::OMEGA => Self::Omega,
186            code_consts::VBYTE_LE => Self::VByteLe,
187            code_consts::VBYTE_BE => Self::VByteBe,
188            code_consts::ZETA2 => Self::Zeta(2),
189            code_consts::ZETA3 => Self::Zeta(3),
190            code_consts::ZETA4 => Self::Zeta(4),
191            code_consts::ZETA5 => Self::Zeta(5),
192            code_consts::ZETA6 => Self::Zeta(6),
193            code_consts::ZETA7 => Self::Zeta(7),
194            code_consts::ZETA8 => Self::Zeta(8),
195            code_consts::ZETA9 => Self::Zeta(9),
196            code_consts::ZETA10 => Self::Zeta(10),
197            code_consts::RICE1 => Self::Rice(1),
198            code_consts::RICE2 => Self::Rice(2),
199            code_consts::RICE3 => Self::Rice(3),
200            code_consts::RICE4 => Self::Rice(4),
201            code_consts::RICE5 => Self::Rice(5),
202            code_consts::RICE6 => Self::Rice(6),
203            code_consts::RICE7 => Self::Rice(7),
204            code_consts::RICE8 => Self::Rice(8),
205            code_consts::RICE9 => Self::Rice(9),
206            code_consts::RICE10 => Self::Rice(10),
207            code_consts::PI1 => Self::Pi(1),
208            code_consts::PI2 => Self::Pi(2),
209            code_consts::PI3 => Self::Pi(3),
210            code_consts::PI4 => Self::Pi(4),
211            code_consts::PI5 => Self::Pi(5),
212            code_consts::PI6 => Self::Pi(6),
213            code_consts::PI7 => Self::Pi(7),
214            code_consts::PI8 => Self::Pi(8),
215            code_consts::PI9 => Self::Pi(9),
216            code_consts::PI10 => Self::Pi(10),
217            code_consts::GOLOMB3 => Self::Golomb(3),
218            code_consts::GOLOMB5 => Self::Golomb(5),
219            code_consts::GOLOMB6 => Self::Golomb(6),
220            code_consts::GOLOMB7 => Self::Golomb(7),
221            code_consts::GOLOMB9 => Self::Golomb(9),
222            code_consts::GOLOMB10 => Self::Golomb(10),
223            code_consts::EXP_GOLOMB1 => Self::ExpGolomb(1),
224            code_consts::EXP_GOLOMB2 => Self::ExpGolomb(2),
225            code_consts::EXP_GOLOMB3 => Self::ExpGolomb(3),
226            code_consts::EXP_GOLOMB4 => Self::ExpGolomb(4),
227            code_consts::EXP_GOLOMB5 => Self::ExpGolomb(5),
228            code_consts::EXP_GOLOMB6 => Self::ExpGolomb(6),
229            code_consts::EXP_GOLOMB7 => Self::ExpGolomb(7),
230            code_consts::EXP_GOLOMB8 => Self::ExpGolomb(8),
231            code_consts::EXP_GOLOMB9 => Self::ExpGolomb(9),
232            code_consts::EXP_GOLOMB10 => Self::ExpGolomb(10),
233            _ => return Err(DispatchError::UnsupportedCodeConst(const_code)),
234        })
235    }
236}
237
238impl DynamicCodeRead for Codes {
239    #[inline]
240    fn read<E: Endianness, CR: CodesRead<E> + ?Sized>(
241        &self,
242        reader: &mut CR,
243    ) -> Result<u64, CR::Error> {
244        Ok(match self.canonicalize() {
245            Codes::Unary => reader.read_unary()?,
246            Codes::Gamma => reader.read_gamma()?,
247            Codes::Delta => reader.read_delta()?,
248            Codes::Omega => reader.read_omega()?,
249            Codes::VByteBe => reader.read_vbyte_be()?,
250            Codes::VByteLe => reader.read_vbyte_le()?,
251            Codes::Zeta(3) => reader.read_zeta3()?,
252            Codes::Zeta(k) => reader.read_zeta(k)?,
253            Codes::Pi(2) => reader.read_pi2()?,
254            Codes::Pi(k) => reader.read_pi(k)?,
255            Codes::Golomb(b) => reader.read_golomb(b)?,
256            Codes::ExpGolomb(k) => reader.read_exp_golomb(k)?,
257            Codes::Rice(log2_b) => reader.read_rice(log2_b)?,
258        })
259    }
260}
261
262impl DynamicCodeWrite for Codes {
263    #[inline]
264    fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
265        &self,
266        writer: &mut CW,
267        n: u64,
268    ) -> Result<usize, CW::Error> {
269        Ok(match self.canonicalize() {
270            Codes::Unary => writer.write_unary(n)?,
271            Codes::Gamma => writer.write_gamma(n)?,
272            Codes::Delta => writer.write_delta(n)?,
273            Codes::Omega => writer.write_omega(n)?,
274            Codes::VByteBe => writer.write_vbyte_be(n)?,
275            Codes::VByteLe => writer.write_vbyte_le(n)?,
276            Codes::Zeta(3) => writer.write_zeta3(n)?,
277            Codes::Zeta(k) => writer.write_zeta(n, k)?,
278            Codes::Pi(2) => writer.write_pi2(n)?,
279            Codes::Pi(k) => writer.write_pi(n, k)?,
280            Codes::Golomb(b) => writer.write_golomb(n, b)?,
281            Codes::ExpGolomb(k) => writer.write_exp_golomb(n, k)?,
282            Codes::Rice(log2_b) => writer.write_rice(n, log2_b)?,
283        })
284    }
285}
286
287impl<E: Endianness, CR: CodesRead<E> + ?Sized> StaticCodeRead<E, CR> for Codes {
288    #[inline(always)]
289    fn read(&self, reader: &mut CR) -> Result<u64, CR::Error> {
290        <Self as DynamicCodeRead>::read(self, reader)
291    }
292}
293
294impl<E: Endianness, CW: CodesWrite<E> + ?Sized> StaticCodeWrite<E, CW> for Codes {
295    #[inline(always)]
296    fn write(&self, writer: &mut CW, n: u64) -> Result<usize, CW::Error> {
297        <Self as DynamicCodeWrite>::write(self, writer, n)
298    }
299}
300
301impl CodeLen for Codes {
302    #[inline]
303    fn len(&self, n: u64) -> usize {
304        match self.canonicalize() {
305            Codes::Unary => n as usize + 1,
306            Codes::Gamma => len_gamma(n),
307            Codes::Delta => len_delta(n),
308            Codes::Omega => len_omega(n),
309            Codes::VByteLe | Codes::VByteBe => bit_len_vbyte(n),
310            Codes::Zeta(k) => len_zeta(n, k),
311            Codes::Pi(k) => len_pi(n, k),
312            Codes::Golomb(b) => len_golomb(n, b),
313            Codes::ExpGolomb(k) => len_exp_golomb(n, k),
314            Codes::Rice(log2_b) => len_rice(n, log2_b),
315        }
316    }
317}
318
319/// Error type for parsing a code from a string.
320#[derive(Debug, Clone)]
321pub enum CodeError {
322    /// Error parsing an integer parameter.
323    ParseError(core::num::ParseIntError),
324    /// Unknown code name. Uses a fixed-size array instead of `String` for `no_std` compatibility.
325    UnknownCode([u8; 32]),
326}
327impl core::error::Error for CodeError {}
328impl core::fmt::Display for CodeError {
329    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
330        match self {
331            CodeError::ParseError(e) => write!(f, "parse error: {}", e),
332            CodeError::UnknownCode(s) => {
333                write!(f, "unknown code: ")?;
334                for c in s {
335                    if *c == 0 {
336                        break;
337                    }
338                    write!(f, "{}", *c as char)?;
339                }
340                Ok(())
341            }
342        }
343    }
344}
345
346impl From<core::num::ParseIntError> for CodeError {
347    fn from(e: core::num::ParseIntError) -> Self {
348        CodeError::ParseError(e)
349    }
350}
351
352impl core::fmt::Display for Codes {
353    fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
354        match self {
355            Codes::Unary => write!(f, "Unary"),
356            Codes::Gamma => write!(f, "Gamma"),
357            Codes::Delta => write!(f, "Delta"),
358            Codes::Omega => write!(f, "Omega"),
359            Codes::VByteBe => write!(f, "VByteBe"),
360            Codes::VByteLe => write!(f, "VByteLe"),
361            Codes::Zeta(k) => write!(f, "Zeta({})", k),
362            Codes::Pi(k) => write!(f, "Pi({})", k),
363            Codes::Golomb(b) => write!(f, "Golomb({})", b),
364            Codes::ExpGolomb(k) => write!(f, "ExpGolomb({})", k),
365            Codes::Rice(log2_b) => write!(f, "Rice({})", log2_b),
366        }
367    }
368}
369
370fn array_format_error(s: &str) -> [u8; 32] {
371    let mut error_buffer = [0u8; 32];
372    let len = s.len().min(32);
373    error_buffer[..len].copy_from_slice(&s.as_bytes()[..len]);
374    error_buffer
375}
376
377impl core::str::FromStr for Codes {
378    type Err = CodeError;
379
380    fn from_str(s: &str) -> Result<Self, Self::Err> {
381        match s {
382            "Unary" => Ok(Codes::Unary),
383            "Gamma" => Ok(Codes::Gamma),
384            "Delta" => Ok(Codes::Delta),
385            "Omega" => Ok(Codes::Omega),
386            "VByteBe" => Ok(Codes::VByteBe),
387            "VByteLe" => Ok(Codes::VByteLe),
388
389            _ => {
390                let mut parts = s.split('(');
391                let name = parts
392                    .next()
393                    .ok_or_else(|| CodeError::UnknownCode(array_format_error(s)))?;
394                let k = parts
395                    .next()
396                    .ok_or_else(|| CodeError::UnknownCode(array_format_error(s)))?
397                    .split(')')
398                    .next()
399                    .ok_or_else(|| CodeError::UnknownCode(array_format_error(s)))?;
400                match name {
401                    "Zeta" => Ok(Codes::Zeta(k.parse()?)),
402                    "Pi" => Ok(Codes::Pi(k.parse()?)),
403                    "Golomb" => Ok(Codes::Golomb(k.parse()?)),
404                    "ExpGolomb" => Ok(Codes::ExpGolomb(k.parse()?)),
405                    "Rice" => Ok(Codes::Rice(k.parse()?)),
406                    _ => Err(CodeError::UnknownCode(array_format_error(name))),
407                }
408            }
409        }
410    }
411}
412
413#[cfg(feature = "serde")]
414impl serde::Serialize for Codes {
415    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
416    where
417        S: serde::Serializer,
418    {
419        serializer.serialize_str(&self.to_string())
420    }
421}
422
423#[cfg(feature = "serde")]
424impl<'de> serde::Deserialize<'de> for Codes {
425    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
426    where
427        D: serde::Deserializer<'de>,
428    {
429        let s = String::deserialize(deserializer)?;
430        s.parse().map_err(serde::de::Error::custom)
431    }
432}
433
434/// Structure representing minimal binary coding with a fixed upper bound.
435///
436/// [Minimal binary coding](crate::codes::minimal_binary) does not
437/// fit the [`Codes`] enum because it is not defined for all integers.
438///
439/// Instances of this structure can be used in contexts in which a
440/// [`DynamicCodeRead`], [`DynamicCodeWrite`], [`StaticCodeRead`],
441/// [`StaticCodeWrite`] or [`CodeLen`] implementing minimal binary coding
442/// is necessary.
443#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
444pub struct MinimalBinary(
445    /// The upper bound of the minimal binary code.
446    pub u64,
447);
448
449impl DynamicCodeRead for MinimalBinary {
450    fn read<E: Endianness, R: CodesRead<E> + ?Sized>(
451        &self,
452        reader: &mut R,
453    ) -> Result<u64, R::Error> {
454        reader.read_minimal_binary(self.0)
455    }
456}
457
458impl DynamicCodeWrite for MinimalBinary {
459    fn write<E: Endianness, W: CodesWrite<E> + ?Sized>(
460        &self,
461        writer: &mut W,
462        n: u64,
463    ) -> Result<usize, W::Error> {
464        writer.write_minimal_binary(n, self.0)
465    }
466}
467
468impl<E: Endianness, CR: CodesRead<E> + ?Sized> StaticCodeRead<E, CR> for MinimalBinary {
469    fn read(&self, reader: &mut CR) -> Result<u64, CR::Error> {
470        <Self as DynamicCodeRead>::read(self, reader)
471    }
472}
473
474impl<E: Endianness, CW: CodesWrite<E> + ?Sized> StaticCodeWrite<E, CW> for MinimalBinary {
475    fn write(&self, writer: &mut CW, n: u64) -> Result<usize, CW::Error> {
476        <Self as DynamicCodeWrite>::write(self, writer, n)
477    }
478}
479
480impl CodeLen for MinimalBinary {
481    fn len(&self, n: u64) -> usize {
482        len_minimal_binary(n, self.0)
483    }
484}