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