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