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}