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