Skip to main content

dsi_bitstream/codes/
params.rs

1/*
2 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
3 * SPDX-FileCopyrightText: 2023 Inria
4 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
5 *
6 * SPDX-License-Identifier: Apache-2.0 OR MIT
7 */
8
9//! Mechanisms for selecting parameters.
10//!
11//! Traits and structures in this module are not normally needed by the typical
12//! user. Their purpose is to provide a systematic way, and in particular
13//! a default way, to select parameters for parameterized traits
14//! such as [`GammaReadParam`] and [`GammaWriteParam`].
15//!
16//! The traits and structure in this module work closely with the bitstream
17//! readers and writers in [`impls`], which have an additional type parameter
18//! `RP`/`WP` that must implement marker traits [`ReadParams`] or
19//! [`WriteParams`], respectively. The type is then used as a selector type to
20//! provide blanket implementations of parameterless traits in [`codes`] such as
21//! [`GammaRead`], [`GammaWrite`], [`DeltaRead`], [`DeltaWrite`], and so on.
22//!
23//! This module provides default selector types [`DefaultReadParams`] and
24//! [`DefaultWriteParams`] which are also the default value for the parameter
25//! `RP`/`WP` in the bitstream readers and writers in [`crate::impls`].
26//! Type-selected blanket implementations of all parameterless traits in
27//! [`crate::codes`] are provided for the bitstream readers and writers in
28//! [`impls`]. Thus, if you do not specify a value for the parameter `RP`/`WP`,
29//! you will obtain automatically the blanket implementations for parameterless
30//! traits contained in this module.
31//!
32//! You can also define a new selector type implementing
33//! [`ReadParams`]/[`WriteParams`] and, within this crate, provide blanket
34//! implementations of the parameterless code traits for the bitstream readers
35//! and writers in [`crate::impls`] with `RP`/`WP` set to that type; a reader or
36//! writer using it then picks up those implementations instead of the ones
37//! provided here. Both the read and write blanket implementations in this
38//! module are provided specifically for
39//! [`DefaultReadParams`]/[`DefaultWriteParams`], so a different selector does
40//! not silently inherit the defaults. Note that, because both the code traits
41//! and the bitstream types are defined in this crate, Rust's orphan rules
42//! prevent a downstream crate from adding such implementations for its own
43//! selector type; a fully custom selector must therefore be added within this
44//! crate.
45//!
46//! Note that the default implementations provided by this module are targeted at
47//! `u32` read words and `u64` write words. If you use different word sizes,
48//! you may want to write your own selector types.
49//!
50//! # Table peek-bits checks
51//!
52//! The `read_*_param` methods in each code module (e.g.,
53//! [`GammaReadParam::read_gamma_param`]) verify at compile time, via `const {
54//! }` blocks using [`BitRead::PEEK_BITS`], that the reader's peek word is large
55//! enough for the table when the corresponding `USE_TABLE` const parameter is
56//! `true`. These checks are short-circuited when the table is not used, so they
57//! are only triggered for the tables actually selected.
58//!
59//! [`impls`]: crate::impls
60//! [`codes`]: crate::codes
61
62use crate::codes::{delta::*, gamma::*, omega::*, pi::*, zeta::*};
63use crate::impls::*;
64use crate::traits::*;
65#[cfg(feature = "mem_dbg")]
66use mem_dbg::{MemDbg, MemSize};
67use num_primitive::PrimitiveNumberAs;
68
69/// Marker trait for read-parameters selector types.
70///
71/// Note that in principle marker traits are not necessary to use
72/// selector types, but they are useful to avoid that the user specifies
73/// a nonsensical type, and to document the meaning of type parameters.
74pub trait ReadParams {}
75
76/// A selector type for read parameters providing reasonable defaults.
77///
78/// If you want to optimize these choices for your architecture, we suggest to
79/// run the benchmarks in the `benches` directory and write your
80/// own implementation.
81#[derive(Debug, Clone)]
82#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
83#[cfg_attr(feature = "mem_dbg", mem_size(flat))]
84pub struct DefaultReadParams;
85impl ReadParams for DefaultReadParams {}
86
87macro_rules! impl_default_read_codes {
88    ($($endianness:ident),*) => {$(
89        impl<WR: WordRead<Word: DoubleType>> GammaRead<$endianness>
90            for BufBitReader<$endianness, WR, DefaultReadParams>
91        {
92            #[inline(always)]
93            fn read_gamma(&mut self) -> Result<u64, Self::Error> {
94                // From our tests on all architectures γ codes are faster
95                // without tables
96                self.read_gamma_param::<false>()
97            }
98        }
99
100        impl<WR: WordRead<Word: DoubleType>> DeltaRead<$endianness>
101            for BufBitReader<$endianness, WR, DefaultReadParams>
102        {
103            #[inline(always)]
104            fn read_delta(&mut self) -> Result<u64, Self::Error> {
105                if cfg!(target_arch = "aarch64") {
106                    self.read_delta_param::<false, false>()
107                } else {
108                    self.read_delta_param::<false, true>()
109                }
110            }
111        }
112
113        impl<WR: WordRead<Word: DoubleType>> OmegaRead<$endianness>
114            for BufBitReader<$endianness, WR, DefaultReadParams>
115        {
116            #[inline(always)]
117            fn read_omega(&mut self) -> Result<u64, Self::Error> {
118                self.read_omega_param::<true>()
119            }
120        }
121
122        impl<WR: WordRead<Word: DoubleType>> ZetaRead<$endianness>
123            for BufBitReader<$endianness, WR, DefaultReadParams>
124        {
125            #[inline(always)]
126            fn read_zeta(&mut self, k: usize) -> Result<u64, Self::Error> {
127                self.read_zeta_param(k)
128            }
129
130            #[inline(always)]
131            fn read_zeta3(&mut self) -> Result<u64, Self::Error> {
132                self.read_zeta3_param::<true>()
133            }
134        }
135
136        impl<WR: WordRead<Word: DoubleType>> PiRead<$endianness>
137            for BufBitReader<$endianness, WR, DefaultReadParams>
138        {
139            #[inline(always)]
140            fn read_pi(&mut self, k: usize) -> Result<u64, Self::Error> {
141                self.read_pi_param(k)
142            }
143
144            #[inline(always)]
145            fn read_pi2(&mut self) -> Result<u64, Self::Error> {
146                self.read_pi2_param::<false>()
147            }
148        }
149
150        impl<WR: WordRead<Word = u64> + WordSeek<Error = <WR as WordRead>::Error>> GammaRead<$endianness>
151            for BitReader<$endianness, WR, DefaultReadParams>
152        {
153            #[inline(always)]
154            fn read_gamma(&mut self) -> Result<u64, Self::Error> {
155                self.read_gamma_param::<true>()
156            }
157        }
158
159        impl<WR: WordRead<Word = u64> + WordSeek<Error = <WR as WordRead>::Error>> DeltaRead<$endianness>
160            for BitReader<$endianness, WR, DefaultReadParams>
161        {
162            #[inline(always)]
163            fn read_delta(&mut self) -> Result<u64, Self::Error> {
164                // <false, true> is better on the universal Zipf distribution
165                self.read_delta_param::<true, true>()
166            }
167        }
168
169        impl<WR: WordRead<Word = u64> + WordSeek<Error = <WR as WordRead>::Error>> OmegaRead<$endianness>
170            for BitReader<$endianness, WR, DefaultReadParams>
171        {
172            #[inline(always)]
173            fn read_omega(&mut self) -> Result<u64, Self::Error> {
174                self.read_omega_param::<true>()
175            }
176        }
177
178        impl<WR: WordRead<Word = u64> + WordSeek<Error = <WR as WordRead>::Error>> ZetaRead<$endianness>
179            for BitReader<$endianness, WR, DefaultReadParams>
180        {
181            #[inline(always)]
182            fn read_zeta(&mut self, k: usize) -> Result<u64, Self::Error> {
183                self.read_zeta_param(k)
184            }
185
186            #[inline(always)]
187            fn read_zeta3(&mut self) -> Result<u64, Self::Error> {
188                self.read_zeta3_param::<true>()
189            }
190        }
191
192        impl<WR: WordRead<Word = u64> + WordSeek<Error = <WR as WordRead>::Error>> PiRead<$endianness>
193            for BitReader<$endianness, WR, DefaultReadParams>
194        {
195            #[inline(always)]
196            fn read_pi(&mut self, k: usize) -> Result<u64, Self::Error> {
197                self.read_pi_param(k)
198            }
199
200            #[inline(always)]
201            fn read_pi2(&mut self) -> Result<u64, Self::Error> {
202                self.read_pi2_param::<true>()
203            }
204        }
205    )*};
206}
207
208impl_default_read_codes! {LittleEndian, BigEndian}
209
210/// Marker trait for write-parameters selector types.
211///
212/// Note that in principle marker traits are not necessary to use
213/// selector types, but they are useful to avoid that the user specifies
214/// a nonsensical type, and to document the meaning of type parameters.
215pub trait WriteParams {}
216
217/// A selector type for write parameters providing reasonable defaults.
218///
219/// If you want to optimize these choices for your architecture, we suggest to
220/// run the benchmarks in the `benches` directory and write your
221/// own implementation.
222#[derive(Debug, Clone)]
223#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
224#[cfg_attr(feature = "mem_dbg", mem_size(flat))]
225pub struct DefaultWriteParams;
226impl WriteParams for DefaultWriteParams {}
227
228macro_rules! impl_default_write_codes {
229    ($($endianness:ident),*) => {$(
230        impl<WR: WordWrite> GammaWrite<$endianness>
231            for BufBitWriter<$endianness, WR, DefaultWriteParams>
232            where u64: PrimitiveNumberAs<WR::Word>,
233        {
234            #[inline(always)]
235            fn write_gamma(&mut self, n: u64) -> Result<usize, Self::Error> {
236                self.write_gamma_param::<true>(n)
237            }
238        }
239
240        impl<WR: WordWrite> DeltaWrite<$endianness>
241            for BufBitWriter<$endianness, WR, DefaultWriteParams>
242            where u64: PrimitiveNumberAs<WR::Word>,
243        {
244            #[inline(always)]
245            fn write_delta(&mut self, n: u64) -> Result<usize, Self::Error> {
246                self.write_delta_param::<true, true>(n)
247            }
248        }
249
250        impl<WR: WordWrite> OmegaWrite<$endianness>
251            for BufBitWriter<$endianness, WR, DefaultWriteParams>
252            where u64: PrimitiveNumberAs<WR::Word>,
253        {
254            #[inline(always)]
255            fn write_omega(&mut self, n: u64) -> Result<usize, Self::Error> {
256                self.write_omega_param::<true>(n)
257            }
258        }
259
260        impl<WR: WordWrite> ZetaWrite<$endianness>
261            for BufBitWriter<$endianness, WR, DefaultWriteParams>
262            where u64: PrimitiveNumberAs<WR::Word>,
263        {
264            #[inline(always)]
265            fn write_zeta(&mut self, n: u64, k: usize) -> Result<usize, Self::Error> {
266                self.write_zeta_param(n, k)
267            }
268
269            #[inline(always)]
270            fn write_zeta3(&mut self, n: u64) -> Result<usize, Self::Error> {
271                self.write_zeta3_param::<true>(n)
272            }
273        }
274
275        impl<WR: WordWrite> PiWrite<$endianness>
276            for BufBitWriter<$endianness, WR, DefaultWriteParams>
277            where u64: PrimitiveNumberAs<WR::Word>,
278        {
279            #[inline(always)]
280            fn write_pi(&mut self, n: u64, k: usize) -> Result<usize, Self::Error> {
281                self.write_pi_param(n, k)
282            }
283
284            #[inline(always)]
285            fn write_pi2(&mut self, n: u64) -> Result<usize, Self::Error> {
286                self.write_pi2_param::<false>(n)
287            }
288        }
289
290    )*};
291}
292
293impl_default_write_codes! {LittleEndian, BigEndian}