Skip to main content

msrtc_rans_core/
source.rs

1// Licensed under the MIT license.
2// Author: Riaan de Beer - github.com/infinityabundance - rdebeer.infinityabundance@gmail.com
3
4//! Source traits for rANS decoder input.
5//!
6//! A source provides units (bytes or words) to the decoder during
7//! initialization and renormalization.
8
9/// Trait for a source that provides rANS units to the decoder.
10///
11/// Equivalent to the `operator(unit_t&)` call on the C++ source type,
12/// simplified to a `bool` return since the decoder uses boolean outcomes.
13pub trait Source<Unit>: Sized {
14    /// Read the next unit from the source.
15    /// Returns `true` if a unit was available, `false` if exhausted.
16    fn read(&mut self, unit: &mut Unit) -> bool;
17
18    /// Check whether the source has been fully consumed.
19    fn is_exhausted(&self) -> bool;
20}
21
22/// A simple slice-based source that reads units from a `&[Unit]`.
23///
24/// Equivalent to the C++ `span<const unit_t>` based source used by
25/// `RansDecoder` and `RansDecoderStreamImpl`.
26#[derive(Debug, Clone)]
27pub struct SliceSource<'a, Unit> {
28    data: &'a [Unit],
29    pos: usize,
30}
31
32impl<'a, Unit: Copy> SliceSource<'a, Unit> {
33    /// Create a new source reading from the given slice.
34    pub fn new(data: &'a [Unit]) -> Self {
35        Self { data, pos: 0 }
36    }
37
38    /// Current read position.
39    pub fn position(&self) -> usize {
40        self.pos
41    }
42
43    /// Remaining units in the source.
44    pub fn remaining(&self) -> usize {
45        self.data.len().saturating_sub(self.pos)
46    }
47
48    /// Seek to an absolute unit position.
49    ///
50    /// Used by persistent stream decoding to continue from a saved cursor.
51    pub fn seek(&mut self, pos: usize) {
52        self.pos = pos.min(self.data.len());
53    }
54}
55
56impl<'a, Unit: Copy> Source<Unit> for SliceSource<'a, Unit> {
57    #[inline]
58    fn read(&mut self, unit: &mut Unit) -> bool {
59        if self.pos < self.data.len() {
60            *unit = self.data[self.pos];
61            self.pos += 1;
62            true
63        } else {
64            false
65        }
66    }
67
68    #[inline]
69    fn is_exhausted(&self) -> bool {
70        self.pos >= self.data.len()
71    }
72}
73
74#[cfg(test)]
75mod tests {
76    use super::*;
77
78    #[test]
79    fn test_slice_source_basic() {
80        let data = [1u8, 2, 3, 4];
81        let mut source = SliceSource::new(&data);
82
83        let mut unit = 0u8;
84        assert!(source.read(&mut unit));
85        assert_eq!(unit, 1);
86        assert!(source.read(&mut unit));
87        assert_eq!(unit, 2);
88        assert!(source.read(&mut unit));
89        assert_eq!(unit, 3);
90        assert!(source.read(&mut unit));
91        assert_eq!(unit, 4);
92
93        assert!(!source.read(&mut unit));
94        assert!(source.is_exhausted());
95    }
96
97    #[test]
98    fn test_empty_source() {
99        let data: [u8; 0] = [];
100        let mut source = SliceSource::new(&data);
101        let mut unit = 0u8;
102        assert!(!source.read(&mut unit));
103        assert!(source.is_exhausted());
104    }
105}