• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

vigna / dsi-bitstream-rs / 22241361593

20 Feb 2026 09:11PM UTC coverage: 53.913% (-0.06%) from 53.968%
22241361593

push

github

vigna
Removed anyhow, rand is optional (implied module gated by feature)

0 of 18 new or added lines in 4 files covered. (0.0%)

2 existing lines in 1 file now uncovered.

2108 of 3910 relevant lines covered (53.91%)

3096788.1 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/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

229
use crate::prelude::Endianness;
230
use crate::prelude::{BitRead, BitWrite};
231

232
use crate::codes::*;
233

234
pub mod codes;
235
pub use codes::*;
236

237
/// Error returned when a code is not supported for dispatch.
238
#[derive(Debug, Clone, Copy)]
239
pub enum DispatchError {
240
    /// The code is not supported for dynamic dispatch.
241
    UnsupportedCode(Codes),
242
    /// The code constant is not supported.
243
    UnsupportedCodeConst(usize),
244
}
245

246
impl core::fmt::Display for DispatchError {
NEW
247
    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
×
NEW
248
        match self {
×
NEW
249
            Self::UnsupportedCode(code) => {
×
NEW
250
                write!(f, "unsupported dispatch for code {:?}", code)
×
251
            }
NEW
252
            Self::UnsupportedCodeConst(c) => {
×
NEW
253
                write!(f, "unsupported code constant {}", c)
×
254
            }
255
        }
256
    }
257
}
258

259
#[cfg(feature = "std")]
260
impl std::error::Error for DispatchError {}
261

262
pub mod r#static;
263
pub use r#static::{ConstCode, code_consts};
264

265
pub mod dynamic;
266
pub use dynamic::{FuncCodeLen, FuncCodeReader, FuncCodeWriter};
267

268
pub mod factory;
269
pub use factory::{CodesReaderFactory, FactoryFuncCodeReader};
270

271
/// Convenience extension trait for reading all the codes supported by the
272
/// library.
273
///
274
/// A blanket implementation is provided for all types that implement the
275
/// necessary traits.
276
///
277
/// This trait is mainly useful internally to implement the dispatch
278
/// traits [`DynamicCodeRead`], [`StaticCodeRead`], [`DynamicCodeWrite`], and
279
/// [`StaticCodeWrite`]. The user might find more useful to define its own
280
/// convenience trait that includes only the codes they need.
281
pub trait CodesRead<E: Endianness>:
282
    BitRead<E>
283
    + GammaRead<E>
284
    + GammaReadParam<E>
285
    + DeltaRead<E>
286
    + DeltaReadParam<E>
287
    + ZetaRead<E>
288
    + ZetaReadParam<E>
289
    + OmegaRead<E>
290
    + MinimalBinaryRead<E>
291
    + PiRead<E>
292
    + GolombRead<E>
293
    + RiceRead<E>
294
    + ExpGolombRead<E>
295
    + VByteBeRead<E>
296
    + VByteLeRead<E>
297
{
298
}
299

300
impl<E: Endianness, B> CodesRead<E> for B where
301
    B: BitRead<E>
302
        + GammaRead<E>
303
        + GammaReadParam<E>
304
        + DeltaRead<E>
305
        + DeltaReadParam<E>
306
        + ZetaRead<E>
307
        + ZetaReadParam<E>
308
        + OmegaRead<E>
309
        + MinimalBinaryRead<E>
310
        + PiRead<E>
311
        + GolombRead<E>
312
        + RiceRead<E>
313
        + ExpGolombRead<E>
314
        + VByteBeRead<E>
315
        + VByteLeRead<E>
316
{
317
}
318

319
/// Convenience extension trait for writing all the codes supported by the
320
/// library.
321
///
322
/// A blanket implementation is provided for all types that implement the
323
/// necessary traits.
324
///
325
/// This trait is mainly useful internally to implement the dispatch
326
/// traits [`DynamicCodeRead`], [`StaticCodeRead`], [`DynamicCodeWrite`], and
327
/// [`StaticCodeWrite`]. The user might find more useful to define its own
328
/// convenience trait that includes only the codes they need.
329
pub trait CodesWrite<E: Endianness>:
330
    BitWrite<E>
331
    + GammaWrite<E>
332
    + DeltaWrite<E>
333
    + ZetaWrite<E>
334
    + OmegaWrite<E>
335
    + MinimalBinaryWrite<E>
336
    + PiWrite<E>
337
    + GolombWrite<E>
338
    + RiceWrite<E>
339
    + ExpGolombWrite<E>
340
    + VByteBeWrite<E>
341
    + VByteLeWrite<E>
342
{
343
}
344

345
impl<E: Endianness, B> CodesWrite<E> for B where
346
    B: BitWrite<E>
347
        + GammaWrite<E>
348
        + DeltaWrite<E>
349
        + ZetaWrite<E>
350
        + OmegaWrite<E>
351
        + MinimalBinaryWrite<E>
352
        + PiWrite<E>
353
        + GolombWrite<E>
354
        + RiceWrite<E>
355
        + ExpGolombWrite<E>
356
        + VByteBeWrite<E>
357
        + VByteLeWrite<E>
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.
365
pub 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.
376
pub trait DynamicCodeWrite {
377
    fn write<E: Endianness, CW: CodesWrite<E> + ?Sized>(
378
        &self,
379
        writer: &mut CW,
380
        value: 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 more specialized,
388
/// as the [`CodesRead`] is a parameter of the trait.
389
///
390
/// For a fixed code this trait may be implemented by storing
391
/// a function pointer.
392
pub trait StaticCodeRead<E: Endianness, CR: CodesRead<E> + ?Sized> {
393
    fn read(&self, reader: &mut CR) -> Result<u64, CR::Error>;
394
}
395

396
/// A trait providing a method to write a code to a [`CodesWrite`] specified as
397
/// a trait type parameter.
398
///
399
/// The difference with [`DynamicCodeWrite`] is that this trait is more specialized,
400
/// as the [`CodesWrite`] is a parameter of the trait.
401
///
402
/// For a fixed code this trait may be implemented by storing a function
403
/// pointer.
404
pub trait StaticCodeWrite<E: Endianness, CW: CodesWrite<E> + ?Sized> {
405
    fn write(&self, writer: &mut CW, value: u64) -> Result<usize, CW::Error>;
406
}
407

408
/// A trait providing a generic method to compute the length of a codeword.
409
pub trait CodeLen {
410
    /// Returns the length of the codeword for `value`.
411
    fn len(&self, value: u64) -> usize;
412
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc