Skip to main content

dsi_bitstream/dispatch/
mod.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//! Programmable static and dynamic dispatch for codes.
10//!
11//! The modules in [codes], such as [`omega`], extend [`BitRead`] and
12//! [`BitWrite`] to provide a way to read and write codes from a bitstream. The
13//! user can thus select at compile time the desired trait and use the
14//! associated codes.
15//!
16//! In many contexts, however, one does not want to commit to a specific set of
17//! codes, but rather would like to write generic methods that accept some code
18//! as an input and then use it to read or write values. For example, a stream
19//! encoder might let the user choose between different codes, depending on the
20//! user's knowledge of the distribution of the values to be encoded.
21//!
22//! Having dynamic selection of a code, however, entails a performance cost, as,
23//! for example, a match statement must be used to select the correct code. To
24//! mitigate this cost, we provide two types of dispatch traits and three types
25//! of implementations based on them.
26//!
27//! # Dispatch Traits
28//!
29//! The traits [`DynamicCodeRead`] and [`DynamicCodeWrite`] are the most generic
30//! ones, and provide a method to read and write a code from a bitstream. By
31//! implementing them, you can write a method accepting one or more unspecified
32//! codes, and operate with them. For example, in this function we read twice a
33//! code and return the sum of the two values, but make no commitment on which
34//! code we will be using:
35//! ```rust
36//! use dsi_bitstream::prelude::*;
37//! use dsi_bitstream::dispatch::{CodesRead, DynamicCodeRead};
38//! use std::fmt::Debug;
39//!
40//! fn read_two_codes_and_sum<
41//!     E: Endianness,
42//!     R: CodesRead<E> + ?Sized,
43//!     GR: DynamicCodeRead
44//! >(
45//!     reader: &mut R,
46//!     code: GR,
47//! ) -> Result<u64, R::Error> {
48//!     Ok(code.read(reader)? + code.read(reader)?)
49//! }
50//! ```
51//! On the other hand, the traits [`StaticCodeRead`] and [`StaticCodeWrite`] are
52//! specialized for a reader or writer of given endianness. This means that they
53//! can in principle be implemented for a specific code by storing a function
54//! pointer, with much less runtime overhead.
55//! ```rust
56//! use dsi_bitstream::prelude::*;
57//! use dsi_bitstream::dispatch::{CodesRead, StaticCodeRead};
58//! use std::fmt::Debug;
59//!
60//! fn read_two_codes_and_sum<
61//!     E: Endianness,
62//!     R: CodesRead<E> + ?Sized,
63//!     SR: StaticCodeRead<E, R>
64//! >(
65//!     reader: &mut R,
66//!     code: SR,
67//! ) -> Result<u64, R::Error> {
68//!     Ok(code.read(reader)? + code.read(reader)?)
69//! }
70//! ```
71//!
72//! Note that the syntax for invoking the methods in the two groups of traits is
73//! identical, but the type variables are on the method in the first case, and
74//! on the trait in the second case.
75//!
76//! # Implementations
77//!
78//! The [`Codes`] enum variants represent all the available codes. [`Codes`]
79//! implements all the dispatch traits, so it can be used to read or write any
80//! code both in a generic and in a specific way. It also implements the
81//! [`CodeLen`] trait, which provides a method to compute the length of a
82//! codeword. The only exception is for [minimal binary codes], which have a
83//! separate [`MinimalBinary`] structure with the same functionality, as they
84//! cannot represent all integers.
85//!
86//! If Rust supported const enums in traits, one could create structures
87//! with const enum type parameters of type [`Codes`], and then the compiler
88//! would be able to optimize away the code selection at compile time. However,
89//! this is not currently possible, so we provide a workaround using a
90//! zero-sized struct with a `const usize` parameter, [`ConstCode`], that
91//! implements all the dispatch traits and [`CodeLen`], and can be used to
92//! select the code at compile time. The parameter must be taken from the
93//! [`code_consts`] module, which contains constants for all parameterless
94//! codes, and for the codes with parameters up to 10. For example, here at
95//! execution time there will be no test to select a code, even if
96//! `read_two_codes_and_sum` is generic:
97//! ```rust
98//! use dsi_bitstream::prelude::*;
99//! use dsi_bitstream::dispatch::{code_consts, CodesRead, DynamicCodeRead};
100//! use std::fmt::Debug;
101//!
102//! fn read_two_codes_and_sum<
103//!     E: Endianness,
104//!     R: CodesRead<E> + ?Sized,
105//!     GR: DynamicCodeRead
106//! >(
107//!     reader: &mut R,
108//!     code: GR,
109//! ) -> Result<u64, R::Error> {
110//!     Ok(code.read(reader)? + code.read(reader)?)
111//! }
112//!
113//! fn call_read_two_codes_and_sum<E: Endianness, R: CodesRead<E> + ?Sized>(
114//!     reader: &mut R,
115//! ) -> Result<u64, R::Error> {
116//!     read_two_codes_and_sum(reader, ConstCode::<{code_consts::GAMMA}>)
117//! }
118//! ```
119//!
120//! Working with [`ConstCode`] is very efficient, but it forces the choice of a
121//! code at compile time. If you need to read or write a code multiple times on
122//! the same type of bitstream, you can use the structs [`FuncCodeReader`] and
123//! [`FuncCodeWriter`], which implement [`StaticCodeRead`] and
124//! [`StaticCodeWrite`] by storing a function pointer.
125//!
126//! A value of type [`FuncCodeReader`] or [`FuncCodeWriter`] can be created by
127//! calling their `new` method with a variant of the [`Codes`] enum. As in the
128//! case of [`ConstCode`], there are pointers for all parameterless codes, and
129//! for the codes with parameters up to 10, and the method will return an error
130//! if the code is not supported.
131//!
132//! For example:
133//! ```rust
134//! use dsi_bitstream::prelude::*;
135//! use dsi_bitstream::dispatch::{CodesRead, StaticCodeRead, FuncCodeReader};
136//! use std::fmt::Debug;
137//!
138//! fn read_two_codes_and_sum<
139//!     E: Endianness,
140//!     R: CodesRead<E> + ?Sized,
141//!     SR: StaticCodeRead<E, R>
142//! >(
143//!     reader: &mut R,
144//!     code: SR,
145//! ) -> Result<u64, R::Error> {
146//!     Ok(code.read(reader)? + code.read(reader)?)
147//! }
148//!
149//! fn call_read_two_codes_and_sum<E: Endianness, R: CodesRead<E> + ?Sized>(
150//!     reader: &mut R,
151//! ) -> Result<u64, R::Error> {
152//!     read_two_codes_and_sum(reader, FuncCodeReader::new(Codes::Gamma).unwrap())
153//! }
154//! ```
155//! Note that we [`unwrap`] the result of the [`new`] method, as we know that a
156//! function pointer exists for the γ code.
157//!
158//! # Workaround to Limitations
159//!
160//! Both [`ConstCode`] and [`FuncCodeReader`] / [`FuncCodeWriter`] are limited
161//! to a fixed set of codes. If you need to work with a code that is not
162//! supported by them, you can implement your own version. For example, here we
163//! define a zero-sized struct that represents a Rice code with a fixed parameter
164//! `LOG2_B`:
165//! ```rust
166//! use dsi_bitstream::prelude::*;
167//! use dsi_bitstream::dispatch::{CodesRead, CodesWrite};
168//! use dsi_bitstream::dispatch::{DynamicCodeRead, DynamicCodeWrite};
169//! use std::fmt::Debug;
170//!
171//! #[derive(Clone, Copy, Debug, Default)]
172//! pub struct Rice<const LOG2_B: usize>;
173//!
174//! impl<const LOG2_B: usize> DynamicCodeRead for Rice<LOG2_B> {
175//!     fn read<E: Endianness, CR: CodesRead<E> + ?Sized>(
176//!         &self,
177//!         reader: &mut CR,
178//!     ) -> Result<u64, CR::Error> {
179//!         reader.read_rice(LOG2_B)
180//!     }
181//! }
182//!
183//! impl<const LOG2_B: usize> DynamicCodeWrite for Rice<LOG2_B> {
184//!     fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
185//!         &self,
186//!         writer: &mut CW,
187//!         n: u64,
188//!     ) -> Result<usize, CW::Error> {
189//!         writer.write_rice(n, LOG2_B)
190//!     }
191//! }
192//!
193//! impl<const LOG2_B: usize> CodeLen for Rice<LOG2_B> {
194//!     #[inline]
195//!     fn len(&self, n: u64) -> usize {
196//!         len_rice(n, LOG2_B)
197//!     }
198//! }
199//! ```
200//!
201//! Suppose instead you need to pass a [`StaticCodeRead`] to a method using a
202//! code that is not supported directly by [`FuncCodeReader`]. You can create a
203//! new [`FuncCodeReader`] using a provided function:
204//! ```rust
205//! use dsi_bitstream::prelude::*;
206//! use dsi_bitstream::dispatch::{CodesRead, StaticCodeRead, FuncCodeReader};
207//! use std::fmt::Debug;
208//!
209//! fn read_two_codes_and_sum<
210//!     E: Endianness,
211//!     R: CodesRead<E> + ?Sized,
212//!     SR: StaticCodeRead<E, R>
213//! >(
214//!     reader: &mut R,
215//!     code: SR,
216//! ) -> Result<u64, R::Error> {
217//!     Ok(code.read(reader)? + code.read(reader)?)
218//! }
219//!
220//! fn call_read_two_codes_and_sum<E: Endianness, R: CodesRead<E> + ?Sized>(
221//!     reader: &mut R,
222//! ) -> Result<u64, R::Error> {
223//!     read_two_codes_and_sum(reader, FuncCodeReader::new_with_func(|r: &mut R| r.read_rice(20)))
224//! }
225//! ```
226//!
227//! [codes]: super::codes
228//! [minimal binary codes]: crate::codes::minimal_binary
229//! [`unwrap`]: core::result::Result::unwrap
230//! [`new`]: FuncCodeReader::new
231
232use crate::prelude::Endianness;
233use crate::prelude::{BitRead, BitWrite};
234
235use crate::codes::*;
236
237pub mod codes;
238pub use codes::*;
239
240/// Error returned when a code is not supported for dispatch.
241#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
242pub enum DispatchError {
243    /// The code is not supported for dynamic dispatch.
244    UnsupportedCode(Codes),
245    /// The code constant is not supported.
246    UnsupportedCodeConst(usize),
247}
248
249impl core::fmt::Display for DispatchError {
250    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
251        match self {
252            Self::UnsupportedCode(code) => {
253                write!(f, "unsupported dispatch for code {:?}", code)
254            }
255            Self::UnsupportedCodeConst(c) => {
256                write!(f, "unsupported code constant {}", c)
257            }
258        }
259    }
260}
261
262impl core::error::Error for DispatchError {}
263
264pub mod r#static;
265pub use r#static::{ConstCode, code_consts};
266
267pub mod dynamic;
268pub use dynamic::{FuncCodeLen, FuncCodeReader, FuncCodeWriter};
269
270pub mod factory;
271pub use factory::{CodesReaderFactory, CodesReaderFactoryHelper, FactoryFuncCodeReader};
272
273/// Convenience extension trait for reading all the codes supported by the
274/// library.
275///
276/// A blanket implementation is provided for all types that implement the
277/// necessary traits.
278///
279/// This trait is mainly useful internally to implement the dispatch
280/// traits [`DynamicCodeRead`], [`StaticCodeRead`], [`DynamicCodeWrite`], and
281/// [`StaticCodeWrite`]. The user might find it more useful to define their own
282/// convenience trait that includes only the codes they need.
283pub trait CodesRead<E: Endianness>:
284    BitRead<E>
285    + GammaRead<E>
286    + DeltaRead<E>
287    + ZetaRead<E>
288    + OmegaRead<E>
289    + PiRead<E>
290    + MinimalBinaryRead<E>
291    + GolombRead<E>
292    + RiceRead<E>
293    + ExpGolombRead<E>
294    + VByteBeRead<E>
295    + VByteLeRead<E>
296{
297}
298
299impl<
300    E: Endianness,
301    B: BitRead<E>
302        + GammaRead<E>
303        + DeltaRead<E>
304        + ZetaRead<E>
305        + OmegaRead<E>
306        + PiRead<E>
307        + MinimalBinaryRead<E>
308        + GolombRead<E>
309        + RiceRead<E>
310        + ExpGolombRead<E>
311        + VByteBeRead<E>
312        + VByteLeRead<E>,
313> CodesRead<E> for B
314{
315}
316
317/// Convenience extension trait for writing all the codes supported by the
318/// library.
319///
320/// A blanket implementation is provided for all types that implement the
321/// necessary traits.
322///
323/// This trait is mainly useful internally to implement the dispatch
324/// traits [`DynamicCodeWrite`] and [`StaticCodeWrite`]. The user might find it
325/// more useful to define their own convenience trait that includes only the
326/// codes they need.
327pub trait CodesWrite<E: Endianness>:
328    BitWrite<E>
329    + GammaWrite<E>
330    + DeltaWrite<E>
331    + ZetaWrite<E>
332    + OmegaWrite<E>
333    + MinimalBinaryWrite<E>
334    + PiWrite<E>
335    + GolombWrite<E>
336    + RiceWrite<E>
337    + ExpGolombWrite<E>
338    + VByteBeWrite<E>
339    + VByteLeWrite<E>
340{
341}
342
343impl<
344    E: Endianness,
345    B: BitWrite<E>
346        + GammaWrite<E>
347        + DeltaWrite<E>
348        + ZetaWrite<E>
349        + OmegaWrite<E>
350        + MinimalBinaryWrite<E>
351        + PiWrite<E>
352        + GolombWrite<E>
353        + RiceWrite<E>
354        + ExpGolombWrite<E>
355        + VByteBeWrite<E>
356        + VByteLeWrite<E>,
357> CodesWrite<E> for B
358{
359}
360
361/// A trait providing a method to read a code from a generic [`CodesRead`].
362///
363/// The difference with [`StaticCodeRead`] is that this trait is more generic,
364/// as the [`CodesRead`] is a parameter of the method, and not of the trait.
365pub trait DynamicCodeRead {
366    fn read<E: Endianness, CR: CodesRead<E> + ?Sized>(
367        &self,
368        reader: &mut CR,
369    ) -> Result<u64, CR::Error>;
370}
371
372/// A trait providing a method to write a code to a generic [`CodesWrite`].
373///
374/// The difference with [`StaticCodeWrite`] is that this trait is more generic,
375/// as the [`CodesWrite`] is a parameter of the method, and not of the trait.
376pub trait DynamicCodeWrite {
377    fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
378        &self,
379        writer: &mut CW,
380        n: u64,
381    ) -> Result<usize, CW::Error>;
382}
383
384/// A trait providing a method to read a code from a [`CodesRead`] specified as
385/// trait type parameter.
386///
387/// The difference with [`DynamicCodeRead`] is that this trait is
388/// more specialized, as the [`CodesRead`] is a parameter of the
389/// trait.
390///
391/// For a fixed code this trait may be implemented by storing
392/// a function pointer.
393pub trait StaticCodeRead<E: Endianness, CR: CodesRead<E> + ?Sized> {
394    fn read(&self, reader: &mut CR) -> Result<u64, CR::Error>;
395}
396
397/// A trait providing a method to write a code to a [`CodesWrite`]
398/// specified as a trait type parameter.
399///
400/// The difference with [`DynamicCodeWrite`] is that this trait is
401/// more specialized, as the [`CodesWrite`] is a parameter of the
402/// trait.
403///
404/// For a fixed code this trait may be implemented by storing a function
405/// pointer.
406pub trait StaticCodeWrite<E: Endianness, CW: CodesWrite<E> + ?Sized> {
407    fn write(&self, writer: &mut CW, n: u64) -> Result<usize, CW::Error>;
408}
409
410/// A trait providing a generic method to compute the length of a codeword.
411pub trait CodeLen {
412    /// Returns the length of the codeword for `n`.
413    #[must_use]
414    fn len(&self, n: u64) -> usize;
415}