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 code traits in [codes](super::codes), such as
12//! [`Omega`](crate::codes::omega), extend [`BitRead`] and [`BitWrite`] to
13//! provide a way to read and write codes from a bitstream. The user can thus
14//! select at compile time the desired trait and use the 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 is an enum whose variants represent all the available
79//! codes. It implements all the dispatch traits, so it can be used to read or
80//! write any code both in a generic and in a specific way. It also implements
81//! the [`CodeLen`] trait, which provides a method to compute the length of a
82//! codeword.
83//!
84//! If Rust would support const enums in traits, one could create structures
85//! with const enum type parameters of type [`Codes`], and then the compiler
86//! would be able to optimize away the code selection at compile time. However,
87//! this is not currently possible, so we provide a workaround using a
88//! zero-sized struct with a `const usize` parameter, [`ConstCode`], that
89//! implements all the dispatch traits and [`CodeLen`], and can be used to
90//! select the code at compile time. The parameter must be taken from the
91//! [`code_consts`] module, which contains constants for all parameterless
92//! codes, and for the codes with parameters up to 10. For example, here at
93//! execution time there will be no test to select a code, even if
94//! `read_two_codes_and_sum` is generic:
95//!```rust
96//! use dsi_bitstream::prelude::*;
97//! use dsi_bitstream::dispatch::{code_consts, CodesRead, DynamicCodeRead};
98//! use std::fmt::Debug;
99//!
100//! fn read_two_codes_and_sum<
101//!     E: Endianness,
102//!     R: CodesRead<E> + ?Sized,
103//!     GR: DynamicCodeRead
104//! >(
105//!     reader: &mut R,
106//!     code: GR,
107//! ) -> Result<u64, R::Error> {
108//!     Ok(code.read(reader)? + code.read(reader)?)
109//! }
110//!
111//! fn call_read_two_codes_and_sum<E: Endianness, R: CodesRead<E> + ?Sized>(
112//!     reader: &mut R,
113//! ) -> Result<u64, R::Error> {
114//!     read_two_codes_and_sum(reader, ConstCode::<{code_consts::GAMMA}>)
115//! }
116//!```
117//!
118//! Working with [`ConstCode`] is very efficient, but it forces the choice of a
119//! code at compile time. If you need to read or write a code multiple times on
120//! the same type of bitstream, you can use the structs [`FuncCodeReader`] and
121//! [`FuncCodeWriter`], which implement [`StaticCodeRead`] and
122//! [`StaticCodeWrite`] by storing a function pointer.
123//!
124//! A value of type [`FuncCodeReader`] or [`FuncCodeWriter`] can be created by
125//! calling their `new` method with a variant of the [`Codes`] enum. As in the
126//! case of [`ConstCode`], there are pointers for all parameterless codes, and
127//! for the codes with parameters up to 10, and the method will return an error
128//! if the code is not supported.
129//!
130//! For example:
131//!```rust
132//! use dsi_bitstream::prelude::*;
133//! use dsi_bitstream::dispatch::{CodesRead, StaticCodeRead, FuncCodeReader};
134//! use std::fmt::Debug;
135//!
136//! fn read_two_codes_and_sum<
137//!     E: Endianness,
138//!     R: CodesRead<E> + ?Sized,
139//!     SR: StaticCodeRead<E, R>
140//! >(
141//!     reader: &mut R,
142//!     code: SR,
143//! ) -> Result<u64, R::Error> {
144//!     Ok(code.read(reader)? + code.read(reader)?)
145//! }
146//!
147//! fn call_read_two_codes_and_sum<E: Endianness, R: CodesRead<E> + ?Sized>(
148//!     reader: &mut R,
149//! ) -> Result<u64, R::Error> {
150//!     read_two_codes_and_sum(reader, FuncCodeReader::new(Codes::Gamma).unwrap())
151//! }
152//!```
153//! Note that we [`unwrap`](core::result::Result::unwrap) the result of the
154//! [`new`](FuncCodeReader::new) method, as we know that a function pointer
155//! exists for the γ code.
156//!
157//! # Workaround to Limitations
158//!
159//! Both [`ConstCode`] and [`FuncCodeReader`] / [`FuncCodeWriter`] are limited
160//! to a fixed set of codes. If you need to work with a code that is not
161//! supported by them, you can implement your own version. For example, here we
162//! define a zero-sized struct that represent a Rice code with a fixed parameter
163//! `LOG2_B`:
164//! ```rust
165//! use dsi_bitstream::prelude::*;
166//! use dsi_bitstream::dispatch::{CodesRead, CodesWrite};
167//! use dsi_bitstream::dispatch::{DynamicCodeRead, DynamicCodeWrite};
168//! use std::fmt::Debug;
169//!
170//! #[derive(Clone, Copy, Debug, Default)]
171//! pub struct Rice<const LOG2_B: usize>;
172//!
173//! impl<const LOG2_B: usize> DynamicCodeRead for Rice<LOG2_B> {
174//!     fn read<E: Endianness, CR: CodesRead<E> + ?Sized>(
175//!         &self,
176//!         reader: &mut CR,
177//!     ) -> Result<u64, CR::Error> {
178//!         reader.read_rice(LOG2_B)
179//!     }
180//! }
181//!
182//! impl<const LOG2_B: usize> DynamicCodeWrite for Rice<LOG2_B> {
183//!     fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
184//!         &self,
185//!         writer: &mut CW,
186//!         value: u64,
187//!     ) -> Result<usize, CW::Error> {
188//!         writer.write_rice(value, LOG2_B)
189//!     }
190//! }
191//!
192//! impl<const LOG2_B: usize> CodeLen for Rice<LOG2_B> {
193//!     #[inline]
194//!     fn len(&self, value: u64) -> usize {
195//!         len_rice(value, LOG2_B)
196//!     }
197//! }
198//! ```
199//!
200//! Suppose instead you need to pass a [`StaticCodeRead`] to a method using a
201//! code that is not supported directly by [`FuncCodeReader`]. You can create a
202//! new [`FuncCodeReader`] using a provided function:
203//!```rust
204//! use dsi_bitstream::prelude::*;
205//! use dsi_bitstream::dispatch::{CodesRead, StaticCodeRead, FuncCodeReader};
206//! use std::fmt::Debug;
207//!
208//! fn read_two_codes_and_sum<
209//!     E: Endianness,
210//!     R: CodesRead<E> + ?Sized,
211//!     SR: StaticCodeRead<E, R>
212//! >(
213//!     reader: &mut R,
214//!     code: SR,
215//! ) -> Result<u64, R::Error> {
216//!     Ok(code.read(reader)? + code.read(reader)?)
217//! }
218//!
219//! fn call_read_two_codes_and_sum<E: Endianness, R: CodesRead<E> + ?Sized>(
220//!     reader: &mut R,
221//! ) -> Result<u64, R::Error> {
222//!     read_two_codes_and_sum(reader, FuncCodeReader::new_with_func(|r: &mut R| r.read_rice(20)))
223//! }
224//!```
225
226use crate::prelude::Endianness;
227use crate::prelude::{BitRead, BitWrite};
228
229use crate::codes::*;
230use anyhow::Result;
231
232pub mod codes;
233pub use codes::*;
234
235pub mod r#static;
236pub use r#static::{code_consts, ConstCode};
237
238pub mod dynamic;
239pub use dynamic::{FuncCodeLen, FuncCodeReader, FuncCodeWriter};
240
241pub mod factory;
242pub use factory::{CodesReaderFactory, FactoryFuncCodeReader};
243
244/// Convenience extension trait for reading all the codes supported by the
245/// library.
246///
247/// A blanket implementation is provided for all types that implement the
248/// necessary traits.
249///
250/// This trait is mainly useful internally to implement the dispatch
251/// traits [`DynamicCodeRead`], [`StaticCodeRead`], [`DynamicCodeWrite`], and
252/// [`StaticCodeWrite`]. The user might find more useful to define its own
253/// convenience trait that includes only the codes they need.
254pub trait CodesRead<E: Endianness>:
255    BitRead<E>
256    + GammaRead<E>
257    + GammaReadParam<E>
258    + DeltaRead<E>
259    + DeltaReadParam<E>
260    + ZetaRead<E>
261    + ZetaReadParam<E>
262    + OmegaRead<E>
263    + MinimalBinaryRead<E>
264    + PiRead<E>
265    + GolombRead<E>
266    + RiceRead<E>
267    + ExpGolombRead<E>
268    + VByteBeRead<E>
269    + VByteLeRead<E>
270{
271}
272
273impl<E: Endianness, B> CodesRead<E> for B where
274    B: BitRead<E>
275        + GammaRead<E>
276        + GammaReadParam<E>
277        + DeltaRead<E>
278        + DeltaReadParam<E>
279        + ZetaRead<E>
280        + ZetaReadParam<E>
281        + OmegaRead<E>
282        + MinimalBinaryRead<E>
283        + PiRead<E>
284        + GolombRead<E>
285        + RiceRead<E>
286        + ExpGolombRead<E>
287        + VByteBeRead<E>
288        + VByteLeRead<E>
289{
290}
291
292/// Convenience extension trait for writing all the codes supported by the
293/// library.
294///
295/// A blanket implementation is provided for all types that implement the
296/// necessary traits.
297///
298/// This trait is mainly useful internally to implement the dispatch
299/// traits [`DynamicCodeRead`], [`StaticCodeRead`], [`DynamicCodeWrite`], and
300/// [`StaticCodeWrite`]. The user might find more useful to define its own
301/// convenience trait that includes only the codes they need.
302pub trait CodesWrite<E: Endianness>:
303    BitWrite<E>
304    + GammaWrite<E>
305    + DeltaWrite<E>
306    + ZetaWrite<E>
307    + OmegaWrite<E>
308    + MinimalBinaryWrite<E>
309    + PiWrite<E>
310    + GolombWrite<E>
311    + RiceWrite<E>
312    + ExpGolombWrite<E>
313    + VByteBeWrite<E>
314    + VByteLeWrite<E>
315{
316}
317
318impl<E: Endianness, B> CodesWrite<E> for B where
319    B: BitWrite<E>
320        + GammaWrite<E>
321        + DeltaWrite<E>
322        + ZetaWrite<E>
323        + OmegaWrite<E>
324        + MinimalBinaryWrite<E>
325        + PiWrite<E>
326        + GolombWrite<E>
327        + RiceWrite<E>
328        + ExpGolombWrite<E>
329        + VByteBeWrite<E>
330        + VByteLeWrite<E>
331{
332}
333
334/// A trait providing a method to read a code from a generic [`CodesRead`].
335///
336/// The difference with [`StaticCodeRead`] is that this trait is more generic,
337/// as the [`CodesRead`] is a parameter of the method, and not of the trait.
338pub trait DynamicCodeRead {
339    fn read<E: Endianness, CR: CodesRead<E> + ?Sized>(
340        &self,
341        reader: &mut CR,
342    ) -> Result<u64, CR::Error>;
343}
344
345/// A trait providing a method to write a code to a generic [`CodesWrite`].
346///
347/// The difference with [`StaticCodeWrite`] is that this trait is more generic,
348/// as the [`CodesWrite`] is a parameter of the method, and not of the trait.
349pub trait DynamicCodeWrite {
350    fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
351        &self,
352        writer: &mut CW,
353        value: u64,
354    ) -> Result<usize, CW::Error>;
355}
356
357/// A trait providing a method to read a code from a [`CodesRead`] specified as
358/// trait type parameter.
359///
360/// The difference with [`DynamicCodeRead`] is that this trait is more specialized,
361/// as the [`CodesRead`] is a parameter of the trait.
362///
363/// For a fixed code this trait may be implemented by storing
364/// a function pointer.
365pub trait StaticCodeRead<E: Endianness, CR: CodesRead<E> + ?Sized> {
366    fn read(&self, reader: &mut CR) -> Result<u64, CR::Error>;
367}
368
369/// A trait providing a method to write a code to a [`CodesWrite`] specified as
370/// a trait type parameter.
371///
372/// The difference with [`DynamicCodeWrite`] is that this trait is more specialized,
373/// as the [`CodesWrite`] is a parameter of the trait.
374///
375/// For a fixed code this trait may be implemented by storing a function
376/// pointer.
377pub trait StaticCodeWrite<E: Endianness, CW: CodesWrite<E> + ?Sized> {
378    fn write(&self, writer: &mut CW, value: u64) -> Result<usize, CW::Error>;
379}
380
381/// A trait providing a generic method to compute the length of a codeword.
382pub trait CodeLen {
383    /// Return the length of the codeword for `value`.
384    fn len(&self, value: u64) -> usize;
385}