1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
 * SPDX-FileCopyrightText: 2023 Tommaso Fontana
 * SPDX-FileCopyrightText: 2023 Inria
 * SPDX-FileCopyrightText: 2023 Sebastiano Vigna
 *
 * SPDX-License-Identifier: Apache-2.0 OR LGPL-2.1-or-later
 */

/*!

Mechanisms for selecting parameters.

Traits and structures in this file are of no interest for the standard
user. Their purpose is to provide a systematic way, and in particular
a default way, to select parameters for parameterized traits
such as [`GammaReadParam`] and [`GammaWriteParam`].

The traits and structure in this module work closely with the
bitstream readers and writers in [`impls`](crate::impls), which have an
additional type parameter `RP`/`WP` that must
implement marker traits [`ReadParams`] or [`WriteParams`], respectively.
The type is then used as a selector type to provide blanket implementations
of parameterless traits in [`codes`](crate::codes) such as [`GammaRead`],
[`GammaWrite`], [`DeltaRead`], [`DeltaWrite`], and so on.

This module provides default selector types [`DefaultReadParams`] and [`DefaultWriteParams`]
which are also the default value for the parameter `RP`/`WP` in the bitstream
readers and writers in [`crate::impls`]. Type-selected blanket implementations
of all parameterless traits in [`crate::codes`] are provided for the bitstream
readers and writers in [`impls`](crate::impls). Thus, if you not specify a value for the
parameter `RP`/`WP`, you will obtain automatically
the blanket implementations for parameterless traits contained in this module.

However, you can also create new selector types implementing [`ReadParams`]/[`WriteParams`] and
write blanket implementations for the bitstream readers and writers in [`crate::impls`]
where `RP`/`WP` is set to your selector types. Then, by specifying your type as value of the
parameter `RP`/`WP` when creating such readers and writers you will use
automatically your blanket implementations instead of the ones provided by this module.

*/

use crate::codes::*;
use crate::impls::*;
use crate::traits::*;
use common_traits::*;
#[cfg(feature = "mem_dbg")]
use mem_dbg::{MemDbg, MemSize};
use std::error::Error;

/// Marker trait for read-parameters selector types.
///
/// Note that in principle marker traits are not necessary to use
/// selector types, but they are useful to avoid that the user specifies
/// a nonsensical type, and to document the meaning of type parameters.
pub trait ReadParams {}

/// A selector type for read parameters providing reasonable defaults.
///
/// If you want to optimize these choices for your architecture, we suggest to
/// run the benchmarks in the `benchmarks` directory and write your
/// own implementation.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
pub struct DefaultReadParams;
impl ReadParams for DefaultReadParams {}

macro_rules! impl_default_read_codes {
    ($($endianess:ident),*) => {$(
        impl<WR: WordRead> GammaRead<$endianess>
            for BufBitReader<$endianess, WR, DefaultReadParams>
        where
            WR:: Word: DoubleType + UpcastableInto<u64>,
            <WR::Word as DoubleType>::DoubleType: CastableInto<u64>,
        {
            #[inline(always)]
            fn read_gamma(&mut self) -> Result<u64, Self::Error> {
                // From our tests, the ARM architecture is faster
                // without tables for ɣ codes.
                return self.read_gamma_param::<false>();
            }
        }

        impl<WR: WordRead> DeltaRead<$endianess>
            for BufBitReader<$endianess, WR, DefaultReadParams>
        where
            WR:: Word: DoubleType + UpcastableInto<u64>,
            <WR::Word as DoubleType>::DoubleType: CastableInto<u64>,
        {
            #[inline(always)]
            fn read_delta(&mut self) -> Result<u64, Self::Error> {
                return self.read_delta_param::<false, true>();
            }
        }

        impl<WR: WordRead> ZetaRead<$endianess>
            for BufBitReader<$endianess, WR, DefaultReadParams>
        where
            WR:: Word: DoubleType + UpcastableInto<u64>,
            <WR::Word as DoubleType>::DoubleType: CastableInto<u64>,
        {
            #[inline(always)]
            fn read_zeta(&mut self, k: u64) -> Result<u64, Self::Error> {
                self.read_zeta_param(k)
            }

            #[inline(always)]
            fn read_zeta3(&mut self) -> Result<u64, Self::Error> {
                self.read_zeta3_param::<true>()
            }
        }

        impl<E: Error + Send + Sync + 'static, WR: WordRead<Error = E, Word = u64> + WordSeek<Error = E>> GammaRead<$endianess>
            for BitReader<$endianess, WR, DefaultReadParams>
        where
            WR:: Word: DoubleType + UpcastableInto<u64>,
            <WR::Word as DoubleType>::DoubleType: CastableInto<u64>,
        {
            #[inline(always)]
            fn read_gamma(&mut self) -> Result<u64, Self::Error> {
                // From our tests, the ARM architecture is faster
                // without tables for ɣ codes.
                return self.read_gamma_param::<false>();
            }
        }

        impl<E: Error + Send + Sync + 'static, WR: WordRead<Error = E, Word = u64> + WordSeek<Error = E>> DeltaRead<$endianess>
            for BitReader<$endianess, WR, DefaultReadParams>
        where
            WR:: Word: DoubleType + UpcastableInto<u64>,
            <WR::Word as DoubleType>::DoubleType: CastableInto<u64>,
        {
            #[inline(always)]
            fn read_delta(&mut self) -> Result<u64, Self::Error> {
                return self.read_delta_param::<false, true>();
            }
        }

        impl<E: Error + Send + Sync + 'static, WR: WordRead<Error = E, Word = u64> + WordSeek<Error = E>> ZetaRead<$endianess>
            for BitReader<$endianess, WR, DefaultReadParams>
        where
            WR:: Word: DoubleType + UpcastableInto<u64>,
            <WR::Word as DoubleType>::DoubleType: CastableInto<u64>,
        {
            #[inline(always)]
            fn read_zeta(&mut self, k: u64) -> Result<u64, Self::Error> {
                self.read_zeta_param(k)
            }

            #[inline(always)]
            fn read_zeta3(&mut self) -> Result<u64, Self::Error> {
                self.read_zeta3_param::<true>()
            }
        }
    )*};
}

impl_default_read_codes! {LittleEndian, BigEndian}

/// Marker trait for write-parameters selector types.
///
/// Note that in principle marker traits are not necessary to use
/// selector types, but they are useful to avoid that the user specifies
/// a nonsensical type, and to document the meaning of type parameters.
pub trait WriteParams {}

/// A selector type for write parameters providing reasonable defaults.
///
/// If you want to optimize these choices for your architecture, we suggest to
/// run the benchmarks in the `benchmarks` directory and write your
/// own implementation.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "mem_dbg", derive(MemDbg, MemSize))]
pub struct DefaultWriteParams;
impl WriteParams for DefaultWriteParams {}

macro_rules! impl_default_write_codes {
    ($($endianess:ident),*) => {$(
        impl<WR: WordWrite> GammaWrite<$endianess>
            for BufBitWriter<$endianess, WR, DefaultWriteParams>
            where u64: CastableInto<WR::Word>,
        {
            #[inline(always)]
            fn write_gamma(&mut self, value: u64) -> Result<usize, Self::Error> {
                self.write_gamma_param::<true>(value)
            }
        }

        impl<WR: WordWrite, DC: WriteParams> DeltaWrite<$endianess>
            for BufBitWriter<$endianess, WR, DC>
            where u64: CastableInto<WR::Word>,
        {
            #[inline(always)]
            fn write_delta(&mut self, value: u64) -> Result<usize, Self::Error> {
                self.write_delta_param::<true, true>(value)
            }
        }

        impl<WR: WordWrite, DC: WriteParams> ZetaWrite<$endianess>
            for BufBitWriter<$endianess, WR, DC>
            where u64: CastableInto<WR::Word>,
        {
            #[inline(always)]
            fn write_zeta(&mut self, value: u64, k: u64) -> Result<usize, Self::Error> {
                self.write_zeta_param::<true>(value, k)
            }

            #[inline(always)]
            fn write_zeta3(&mut self, value: u64) -> Result<usize, Self::Error> {
                self.write_zeta3_param::<true>(value)
            }
        }

    )*};
}

impl_default_write_codes! {LittleEndian, BigEndian}