Skip to main content

cadmpeg_core/decode/
view.rs

1// SPDX-License-Identifier: Apache-2.0
2//! Bounded navigation over one address space.
3
4use crate::cursor::bounded_len;
5
6use super::error::SourceLocation;
7use super::probe::{ParseError, ParseErrorKind};
8use super::space::SpaceId;
9
10/// A count proven to fit in the unread input.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub struct BoundedCount(usize);
13
14impl BoundedCount {
15    /// Returns the proven element count.
16    pub fn get(self) -> usize {
17        self.0
18    }
19}
20
21/// A bounded window over one address space.
22#[derive(Debug, Clone, Copy)]
23pub struct View<'a> {
24    bytes: &'a [u8],
25    space: SpaceId,
26    start: usize,
27    end: usize,
28    position: usize,
29}
30
31impl<'a> View<'a> {
32    /// Creates a full-window view over an entire space buffer.
33    pub(crate) fn over_space(bytes: &'a [u8], space: SpaceId) -> View<'a> {
34        View {
35            bytes,
36            space,
37            start: 0,
38            end: bytes.len(),
39            position: 0,
40        }
41    }
42
43    /// Returns the space this view navigates.
44    pub(crate) fn space(self) -> SpaceId {
45        self.space
46    }
47
48    /// Returns the absolute position within the space.
49    pub fn position(self) -> usize {
50        self.position
51    }
52
53    /// Returns the window's inclusive lower bound.
54    pub fn start(self) -> usize {
55        self.start
56    }
57
58    /// Returns the window's exclusive upper bound.
59    pub fn end(self) -> usize {
60        self.end
61    }
62
63    /// Returns the number of unread bytes before the window's end.
64    pub fn remaining(self) -> usize {
65        self.end.saturating_sub(self.position)
66    }
67
68    /// Returns this view's current source location.
69    pub fn location(self) -> SourceLocation {
70        SourceLocation {
71            space: self.space,
72            offset: self.position as u64,
73        }
74    }
75
76    /// Returns a source location at an absolute offset in this view's space.
77    pub fn location_at(self, offset: usize) -> SourceLocation {
78        SourceLocation {
79            space: self.space,
80            offset: offset as u64,
81        }
82    }
83
84    /// Returns the readable window `start..end` as a slice.
85    pub fn window(self) -> &'a [u8] {
86        self.bytes.get(self.start..self.end).unwrap_or_default()
87    }
88
89    /// Takes `count` bytes, advancing only on success.
90    pub fn take(&mut self, count: usize) -> Option<&'a [u8]> {
91        let end = self.position.checked_add(count)?;
92        if end > self.end {
93            return None;
94        }
95        let bytes = self.bytes.get(self.position..end)?;
96        self.position = end;
97        Some(bytes)
98    }
99
100    /// Takes a fixed-size byte array, advancing only on success.
101    pub fn array<const N: usize>(&mut self) -> Option<[u8; N]> {
102        self.take(N)?.try_into().ok()
103    }
104
105    /// Skips exactly `count` bytes.
106    pub fn skip(&mut self, count: usize) -> Option<()> {
107        self.take(count).map(|_| ())
108    }
109
110    /// Moves to an absolute offset, honoring the stored lower bound.
111    pub fn seek(&mut self, position: usize) -> Option<()> {
112        (self.start <= position && position <= self.end).then(|| self.position = position)
113    }
114
115    /// Reads a single byte.
116    pub fn u8(&mut self) -> Option<u8> {
117        self.array::<1>().map(|[value]| value)
118    }
119
120    /// Returns an exactly contained child window.
121    pub fn child(self, start: usize, end: usize) -> Option<View<'a>> {
122        if self.start <= start && start <= end && end <= self.end {
123            Some(View {
124                bytes: self.bytes,
125                space: self.space,
126                start,
127                end,
128                position: start,
129            })
130        } else {
131            None
132        }
133    }
134
135    /// Proves a declared element count could fit in the unread bytes.
136    pub fn counted(self, count: u64, min_element_size: usize) -> Option<BoundedCount> {
137        bounded_len(count, min_element_size, self.remaining()).map(BoundedCount)
138    }
139
140    /// Builds an unexpected-eof error from the view's current state.
141    fn eof(self, needed: u64) -> ParseError {
142        ParseError {
143            location: self.location(),
144            kind: ParseErrorKind::UnexpectedEof {
145                needed,
146                remaining: self.remaining() as u64,
147            },
148            operation: "required_read",
149        }
150    }
151
152    /// Required-read mirror of [`View::take`].
153    pub fn req_take(&mut self, count: usize) -> Result<&'a [u8], ParseError> {
154        match self.take(count) {
155            Some(bytes) => Ok(bytes),
156            None => Err(self.eof(count as u64)),
157        }
158    }
159
160    /// Required-read mirror of [`View::u8`].
161    pub fn req_u8(&mut self) -> Result<u8, ParseError> {
162        match self.u8() {
163            Some(value) => Ok(value),
164            None => Err(self.eof(1)),
165        }
166    }
167}
168
169macro_rules! view_readers {
170    ($(($probe:ident, $req:ident, $ty:ty, $conv:ident, $size:literal)),* $(,)?) => {
171        impl View<'_> {
172            $(
173                #[doc = concat!("Probe read of a `", stringify!($ty), "` via `", stringify!($conv), "`.")]
174                pub fn $probe(&mut self) -> Option<$ty> {
175                    self.array::<$size>().map(<$ty>::$conv)
176                }
177
178                #[doc = concat!("Required-read mirror of [`View::", stringify!($probe), "`].")]
179                pub fn $req(&mut self) -> Result<$ty, ParseError> {
180                    match self.array::<$size>() {
181                        Some(bytes) => Ok(<$ty>::$conv(bytes)),
182                        None => Err(self.eof($size)),
183                    }
184                }
185            )*
186        }
187    };
188}
189
190view_readers!(
191    (u16_le, req_u16_le, u16, from_le_bytes, 2),
192    (i16_le, req_i16_le, i16, from_le_bytes, 2),
193    (u32_le, req_u32_le, u32, from_le_bytes, 4),
194    (i32_le, req_i32_le, i32, from_le_bytes, 4),
195    (u64_le, req_u64_le, u64, from_le_bytes, 8),
196    (i64_le, req_i64_le, i64, from_le_bytes, 8),
197    (f32_le, req_f32_le, f32, from_le_bytes, 4),
198    (f64_le, req_f64_le, f64, from_le_bytes, 8),
199    (u16_be, req_u16_be, u16, from_be_bytes, 2),
200    (u32_be, req_u32_be, u32, from_be_bytes, 4),
201    (u64_be, req_u64_be, u64, from_be_bytes, 8),
202    (f64_be, req_f64_be, f64, from_be_bytes, 8),
203);