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