1#![warn(clippy::indexing_slicing, clippy::arithmetic_side_effects)]
4
5use super::error::SourceLocation;
6use super::probe::{ParseError, ParseErrorKind};
7use super::space::SpaceId;
8
9pub fn bounded_len(count: u64, element_size: usize, remaining: usize) -> Option<usize> {
16 if element_size == 0 {
17 return None;
18 }
19 let count = usize::try_from(count).ok()?;
20 let bytes = count.checked_mul(element_size)?;
21 (bytes <= remaining).then_some(count)
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub struct BoundedCount(usize);
27
28impl BoundedCount {
29 pub fn get(self) -> usize {
31 self.0
32 }
33}
34
35#[derive(Debug, Clone, Copy)]
37pub struct View<'a> {
38 bytes: &'a [u8],
39 space: SpaceId,
40 start: usize,
41 end: usize,
42 position: usize,
43}
44
45impl<'a> View<'a> {
46 pub(crate) fn over_space(bytes: &'a [u8], space: SpaceId) -> View<'a> {
48 View {
49 bytes,
50 space,
51 start: 0,
52 end: bytes.len(),
53 position: 0,
54 }
55 }
56
57 pub fn over_retained(bytes: &'a [u8]) -> View<'a> {
63 Self::over_space(bytes, SpaceId::ROOT)
64 }
65
66 pub(crate) fn space(self) -> SpaceId {
68 self.space
69 }
70
71 pub fn position(self) -> usize {
73 self.position
74 }
75
76 pub fn start(self) -> usize {
78 self.start
79 }
80
81 pub fn end(self) -> usize {
83 self.end
84 }
85
86 pub fn remaining(self) -> usize {
88 self.end.saturating_sub(self.position)
89 }
90
91 pub fn is_empty(self) -> bool {
93 self.remaining() == 0
94 }
95
96 pub fn location(self) -> SourceLocation {
98 SourceLocation {
99 space: self.space,
100 offset: self.position as u64,
101 }
102 }
103
104 pub fn location_at(self, offset: usize) -> SourceLocation {
106 SourceLocation {
107 space: self.space,
108 offset: offset as u64,
109 }
110 }
111
112 pub fn window(self) -> &'a [u8] {
114 self.bytes.get(self.start..self.end).unwrap_or_default()
115 }
116
117 pub fn take(&mut self, count: usize) -> Option<&'a [u8]> {
119 let end = self.position.checked_add(count)?;
120 if end > self.end {
121 return None;
122 }
123 let bytes = self.bytes.get(self.position..end)?;
124 self.position = end;
125 Some(bytes)
126 }
127
128 pub fn array<const N: usize>(&mut self) -> Option<[u8; N]> {
130 self.take(N)?.try_into().ok()
131 }
132
133 pub fn skip(&mut self, count: usize) -> Option<()> {
135 self.take(count).map(|_| ())
136 }
137
138 pub fn seek(&mut self, position: usize) -> Option<()> {
140 (self.start <= position && position <= self.end).then(|| self.position = position)
141 }
142
143 pub fn u8(&mut self) -> Option<u8> {
145 self.array::<1>().map(|[value]| value)
146 }
147
148 pub fn child(self, start: usize, end: usize) -> Option<View<'a>> {
150 if self.start <= start && start <= end && end <= self.end {
151 Some(View {
152 bytes: self.bytes,
153 space: self.space,
154 start,
155 end,
156 position: start,
157 })
158 } else {
159 None
160 }
161 }
162
163 pub fn counted(self, count: u64, min_element_size: usize) -> Option<BoundedCount> {
165 bounded_len(count, min_element_size, self.remaining()).map(BoundedCount)
166 }
167
168 pub fn read_counted<T>(
173 &mut self,
174 count: u64,
175 element_size: usize,
176 mut read: impl FnMut(&mut Self) -> Option<T>,
177 ) -> Option<Vec<T>> {
178 let count = self.counted(count, element_size)?.get();
179 let mut values = Vec::with_capacity(count);
180 for _ in 0..count {
181 values.push(read(self)?);
182 }
183 Some(values)
184 }
185
186 pub fn utf16_le(&mut self, count: usize) -> Option<String> {
191 let units = self.read_counted(u64::try_from(count).ok()?, 2, View::u16_le)?;
192 String::from_utf16(&units).ok()
193 }
194
195 pub fn utf16le_at(bytes: &[u8], offset: usize, count: usize) -> Option<(String, usize)> {
201 let mut view = View::over_retained(bytes);
202 view.seek(offset)?;
203 let units = view.read_counted(u64::try_from(count).ok()?, 2, View::u16_le)?;
204 Some((String::from_utf16(&units).ok()?, view.position()))
205 }
206
207 fn eof(self, needed: u64) -> ParseError {
209 ParseError {
210 location: self.location(),
211 kind: ParseErrorKind::UnexpectedEof {
212 needed,
213 remaining: self.remaining() as u64,
214 },
215 operation: "required_read",
216 }
217 }
218
219 pub fn req_take(&mut self, count: usize) -> Result<&'a [u8], ParseError> {
221 match self.take(count) {
222 Some(bytes) => Ok(bytes),
223 None => Err(self.eof(count as u64)),
224 }
225 }
226
227 pub fn req_u8(&mut self) -> Result<u8, ParseError> {
229 match self.u8() {
230 Some(value) => Ok(value),
231 None => Err(self.eof(1)),
232 }
233 }
234}
235
236macro_rules! view_readers {
237 ($(($probe:ident, $req:ident, $at:ident, $ty:ty, $conv:ident, $size:literal)),* $(,)?) => {
238 impl View<'_> {
239 $(
240 #[doc = concat!("Probe read of a `", stringify!($ty), "` via `", stringify!($conv), "`.")]
241 pub fn $probe(&mut self) -> Option<$ty> {
242 self.array::<$size>().map(<$ty>::$conv)
243 }
244
245 #[doc = concat!("Required-read mirror of [`View::", stringify!($probe), "`].")]
246 pub fn $req(&mut self) -> Result<$ty, ParseError> {
247 match self.array::<$size>() {
248 Some(bytes) => Ok(<$ty>::$conv(bytes)),
249 None => Err(self.eof($size)),
250 }
251 }
252
253 #[doc = concat!(
254 "Offset probe of a `",
255 stringify!($ty),
256 "` over retained bytes. Sequential readers should keep a live `View` and call [`View::",
257 stringify!($probe),
258 "`] instead."
259 )]
260 pub fn $at(bytes: &[u8], offset: usize) -> Option<$ty> {
261 let mut view = View::over_retained(bytes);
262 view.seek(offset)?;
263 view.$probe()
264 }
265 )*
266 }
267 };
268}
269
270view_readers!(
271 (u16_le, req_u16_le, u16_le_at, u16, from_le_bytes, 2),
272 (i16_le, req_i16_le, i16_le_at, i16, from_le_bytes, 2),
273 (u32_le, req_u32_le, u32_le_at, u32, from_le_bytes, 4),
274 (i32_le, req_i32_le, i32_le_at, i32, from_le_bytes, 4),
275 (u64_le, req_u64_le, u64_le_at, u64, from_le_bytes, 8),
276 (i64_le, req_i64_le, i64_le_at, i64, from_le_bytes, 8),
277 (f32_le, req_f32_le, f32_le_at, f32, from_le_bytes, 4),
278 (f64_le, req_f64_le, f64_le_at, f64, from_le_bytes, 8),
279 (u16_be, req_u16_be, u16_be_at, u16, from_be_bytes, 2),
280 (i16_be, req_i16_be, i16_be_at, i16, from_be_bytes, 2),
281 (u32_be, req_u32_be, u32_be_at, u32, from_be_bytes, 4),
282 (i32_be, req_i32_be, i32_be_at, i32, from_be_bytes, 4),
283 (u64_be, req_u64_be, u64_be_at, u64, from_be_bytes, 8),
284 (i64_be, req_i64_be, i64_be_at, i64, from_be_bytes, 8),
285 (f32_be, req_f32_be, f32_be_at, f32, from_be_bytes, 4),
286 (f64_be, req_f64_be, f64_be_at, f64, from_be_bytes, 8),
287);
288
289#[cfg(test)]
290mod tests {
291 use super::{bounded_len, BoundedCount, View};
292 use crate::decode::space::SpaceId;
293
294 #[test]
295 fn read_counted_allocates_only_plausible_lengths() {
296 let payload = [1u8, 0, 0, 0, 2, 0, 0, 0];
297 let mut view = View::over_space(&payload, SpaceId::ROOT);
298 let values = view
299 .read_counted(2, 4, View::u32_le)
300 .expect("two fixture values");
301 assert_eq!(values, [1, 2]);
302 let mut view = View::over_space(&payload, SpaceId::ROOT);
303 assert_eq!(
304 view.read_counted(u64::from(u32::MAX), 4, View::u32_le),
305 None
306 );
307 }
308
309 #[test]
310 fn counted_rejects_impossible_counts() {
311 let payload = [0u8; 40];
312 let view = View::over_space(&payload, SpaceId::ROOT);
313 assert_eq!(view.counted(10, 4).map(BoundedCount::get), Some(10));
314 assert_eq!(view.counted(11, 4), None);
315 assert_eq!(bounded_len(u64::MAX, 20, usize::MAX), None);
316 }
317
318 #[test]
319 fn seek_honors_lower_bound() {
320 let payload = [0u8; 8];
321 let mut view = View::over_space(&payload, SpaceId::ROOT)
322 .child(2, 6)
323 .expect("valid child");
324 assert_eq!(view.seek(0), None);
325 assert_eq!(view.seek(2), Some(()));
326 assert_eq!(view.seek(6), Some(()));
327 assert_eq!(view.seek(7), None);
328 }
329
330 #[test]
331 fn offset_helpers_match_live_reads() {
332 let mut bytes = Vec::new();
333 bytes.extend_from_slice(&0x0102_u16.to_le_bytes());
334 bytes.extend_from_slice(&0x0304_u16.to_be_bytes());
335 bytes.extend_from_slice(&(-5_i16).to_be_bytes());
336 bytes.extend_from_slice(&1.5_f32.to_le_bytes());
337 assert_eq!(View::u16_le_at(&bytes, 0), Some(0x0102));
338 assert_eq!(View::u16_be_at(&bytes, 2), Some(0x0304));
339 assert_eq!(View::i16_be_at(&bytes, 4), Some(-5));
340 assert_eq!(View::f32_le_at(&bytes, 6), Some(1.5));
341 assert_eq!(View::u16_le_at(&bytes, bytes.len()), None);
342 let mut view = View::over_space(&bytes, SpaceId::ROOT);
343 assert_eq!(view.u16_le(), Some(0x0102));
344 assert_eq!(view.u16_be(), Some(0x0304));
345 assert_eq!(view.i16_be(), Some(-5));
346 assert_eq!(view.f32_le(), Some(1.5));
347 }
348
349 #[test]
350 fn utf16le_matches_le_helper() {
351 assert_eq!(
352 View::utf16le_at(b"A\0B\0", 0, 2),
353 Some(("AB".to_string(), 4))
354 );
355 assert_eq!(View::utf16le_at(b"A\0B\0", 0, 3), None);
356 let mut view = View::over_space(b"A\0B\0", SpaceId::ROOT);
357 assert_eq!(view.utf16_le(2), Some("AB".to_string()));
358 assert_eq!(view.position(), 4);
359 }
360}