Skip to main content

dsi_bitstream/codes/
gamma.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//! Elias γ code.
10//!
11//! The γ code of a natural number *n* is the concatenation of the unary code of
12//! ⌊log₂(*n* + 1)⌋ and of the binary representation of *n* + 1 with the most
13//! significant bit removed.
14//!
15//! The implied distribution of the γ code is ≈ 1/2*x*².
16//!
17//! The `USE_TABLE` parameter enables or disables the use of pre-computed tables
18//! for decoding.
19//!
20//! The supported range is [0 . . 2⁶⁴ – 1).
21//!
22//! # References
23//!
24//! Peter Elias, “[Universal codeword sets and representations of the
25//! integers]”. IEEE Transactions on Information Theory, 21(2):194–203, March
26//! 1975.
27//!
28//! [Universal codeword sets and representations of the integers]: https://doi.org/10.1109/TIT.1975.1055349
29
30use super::gamma_tables;
31use crate::traits::*;
32
33/// Returns the length of the γ code for `n`.
34#[must_use]
35#[inline(always)]
36pub const fn len_gamma_param<const USE_TABLE: bool>(mut n: u64) -> usize {
37    debug_assert!(n < u64::MAX);
38    if USE_TABLE {
39        // We cannot use .get() here because n is a u64
40        if n < gamma_tables::LEN.len() as u64 {
41            return gamma_tables::LEN[n as usize] as usize;
42        }
43    }
44    n += 1;
45    let λ = n.ilog2();
46    2 * λ as usize + 1
47}
48
49/// Returns the length of the γ code for `n` using
50/// a default value for `USE_TABLE`.
51#[must_use]
52#[inline(always)]
53pub const fn len_gamma(n: u64) -> usize {
54    #[cfg(target_arch = "arm")]
55    return len_gamma_param::<false>(n);
56    #[cfg(not(target_arch = "arm"))]
57    return len_gamma_param::<true>(n);
58}
59
60/// Trait for reading γ codes.
61///
62/// This is the trait you should usually pull into scope to read γ codes.
63pub trait GammaRead<E: Endianness>: BitRead<E> {
64    fn read_gamma(&mut self) -> Result<u64, Self::Error>;
65}
66
67/// Parametric trait for reading γ codes.
68///
69/// This trait is more general than [`GammaRead`], as it makes it possible
70/// to specify how to use tables using const parameters.
71///
72/// We provide an implementation of this trait for [`BitRead`]. An implementation
73/// of [`GammaRead`] using default values is usually provided exploiting the
74/// [`crate::codes::params::ReadParams`] mechanism.
75pub trait GammaReadParam<E: Endianness>: BitRead<E> {
76    fn read_gamma_param<const USE_TABLE: bool>(&mut self) -> Result<u64, Self::Error>;
77}
78
79/// Default, internal non-table based implementation that works
80/// for any endianness.
81#[inline(always)]
82fn default_read_gamma<E: Endianness, B: BitRead<E>>(backend: &mut B) -> Result<u64, B::Error> {
83    let len = backend.read_unary()?;
84    debug_assert!(len < 64);
85    Ok(backend.read_bits(len as usize)? + (1 << len) - 1)
86}
87
88impl<B: BitRead<BE>> GammaReadParam<BE> for B {
89    #[inline(always)]
90    fn read_gamma_param<const USE_TABLE: bool>(&mut self) -> Result<u64, Self::Error> {
91        const {
92            if USE_TABLE {
93                gamma_tables::check_read_table(B::PEEK_BITS)
94            }
95        }
96        if USE_TABLE {
97            if let Some((res, _)) = gamma_tables::read_table_be(self) {
98                return Ok(res);
99            }
100        }
101        default_read_gamma(self)
102    }
103}
104
105impl<B: BitRead<LE>> GammaReadParam<LE> for B {
106    #[inline(always)]
107    fn read_gamma_param<const USE_TABLE: bool>(&mut self) -> Result<u64, Self::Error> {
108        const {
109            if USE_TABLE {
110                gamma_tables::check_read_table(B::PEEK_BITS)
111            }
112        }
113        if USE_TABLE {
114            if let Some((res, _)) = gamma_tables::read_table_le(self) {
115                return Ok(res);
116            }
117        }
118        default_read_gamma(self)
119    }
120}
121
122/// Trait for writing γ codes.
123///
124/// This is the trait you should usually pull into scope to write γ codes.
125pub trait GammaWrite<E: Endianness>: BitWrite<E> {
126    fn write_gamma(&mut self, n: u64) -> Result<usize, Self::Error>;
127}
128
129/// Parametric trait for writing γ codes.
130///
131/// This trait is more general than [`GammaWrite`], as it makes it possible
132/// to specify how to use tables using const parameters.
133///
134/// We provide an implementation of this trait for [`BitWrite`]. An implementation
135/// of [`GammaWrite`] using default values is usually provided exploiting the
136/// [`crate::codes::params::WriteParams`] mechanism.
137pub trait GammaWriteParam<E: Endianness>: BitWrite<E> {
138    fn write_gamma_param<const USE_TABLE: bool>(&mut self, n: u64) -> Result<usize, Self::Error>;
139}
140
141impl<B: BitWrite<BE>> GammaWriteParam<BE> for B {
142    #[inline(always)]
143    #[allow(clippy::collapsible_if)]
144    fn write_gamma_param<const USE_TABLE: bool>(&mut self, n: u64) -> Result<usize, Self::Error> {
145        if USE_TABLE {
146            if let Some(len) = gamma_tables::write_table_be(self, n)? {
147                return Ok(len);
148            }
149        }
150        default_write_gamma(self, n)
151    }
152}
153
154impl<B: BitWrite<LE>> GammaWriteParam<LE> for B {
155    #[inline(always)]
156    #[allow(clippy::collapsible_if)]
157    fn write_gamma_param<const USE_TABLE: bool>(&mut self, n: u64) -> Result<usize, Self::Error> {
158        if USE_TABLE {
159            if let Some(len) = gamma_tables::write_table_le(self, n)? {
160                return Ok(len);
161            }
162        }
163        default_write_gamma(self, n)
164    }
165}
166
167/// Default, internal non-table based implementation that works
168/// for any endianness.
169#[inline(always)]
170fn default_write_gamma<E: Endianness, B: BitWrite<E>>(
171    backend: &mut B,
172    mut n: u64,
173) -> Result<usize, B::Error> {
174    debug_assert!(n < u64::MAX);
175    n += 1;
176    let λ = n.ilog2();
177
178    #[cfg(feature = "checks")]
179    {
180        // Clean up n in case checks are enabled
181        n ^= 1 << λ;
182    }
183
184    Ok(backend.write_unary(λ as _)? + backend.write_bits(n, λ as _)?)
185}