1#![deny(clippy::disallowed_methods, clippy::disallowed_types)]
10
11use std::{fmt, ops::Range};
12
13#[derive(Clone, Copy, Debug, PartialEq, Eq)]
16pub enum BoundsError {
17 LengthOverflow {
19 offset: usize,
21 origin: usize,
23 count: usize,
25 stride: usize,
27 },
28 OutOfBounds {
30 offset: usize,
32 origin: usize,
34 length: usize,
36 total: usize,
38 },
39}
40impl BoundsError {
41 fn with_origin(self, origin: usize) -> Self {
42 match self {
43 Self::LengthOverflow {
44 offset,
45 count,
46 stride,
47 ..
48 } => Self::LengthOverflow {
49 offset,
50 origin,
51 count,
52 stride,
53 },
54 Self::OutOfBounds {
55 offset,
56 length,
57 total,
58 ..
59 } => Self::OutOfBounds {
60 offset,
61 origin,
62 length,
63 total,
64 },
65 }
66 }
67 pub fn absolute(&self) -> u128 {
69 let (offset, origin) = match *self {
70 Self::LengthOverflow { offset, origin, .. } | Self::OutOfBounds { offset, origin, .. } => {
71 (offset, origin)
72 }
73 };
74 (offset as u128).saturating_add(origin as u128)
76 }
77}
78impl fmt::Display for BoundsError {
79 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80 let offset = self.absolute();
81 match self {
82 Self::LengthOverflow { count, stride, .. } => write!(
83 f,
84 "byte span at {offset:#x}: {count} entries of {stride} bytes overflow"
85 ),
86 Self::OutOfBounds { length, total, .. } => write!(
87 f,
88 "byte span at {offset:#x}: {length} bytes outside a {total}-byte view"
89 ),
90 }
91 }
92}
93impl std::error::Error for BoundsError {}
94
95#[derive(Clone, Copy, Debug, PartialEq, Eq)]
97pub struct FieldError {
98 pub what: &'static str,
100 pub bounds: BoundsError,
102}
103impl fmt::Display for FieldError {
104 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
105 let (what, at) = (self.what, self.bounds.absolute());
106 match self.bounds {
107 BoundsError::LengthOverflow { count, stride, .. } => {
108 write!(f, "{what} at {at:#x}: {count} entries of {stride} bytes overflow")
109 }
110 BoundsError::OutOfBounds { length, total, .. } => {
111 write!(f, "{what} at {at:#x}: {length} bytes run past the end of {total}")
112 }
113 }
114 }
115}
116impl std::error::Error for FieldError {}
117
118pub trait Field<T> {
120 fn field(self, what: &'static str) -> Result<T, FieldError>;
122}
123impl<T> Field<T> for Result<T, BoundsError> {
124 fn field(self, what: &'static str) -> Result<T, FieldError> {
125 self.map_err(|bounds| FieldError { what, bounds })
126 }
127}
128
129#[derive(Clone, Copy, Debug, PartialEq, Eq)]
131pub enum Endian {
132 Little,
134 Big,
136}
137
138macro_rules! numbers {
140 ($($name:ident: $t:ty),* $(,)?) => {
141 impl View<'_> {
142 $(
143 #[doc = concat!("The `", stringify!($t), "` at `offset`, in byte order `e`.")]
144 #[inline]
145 pub fn $name(&self, offset: usize, e: Endian) -> Result<$t, BoundsError> {
146 let b = self.array(offset)?;
147 Ok(match e {
148 Endian::Little => <$t>::from_le_bytes(b),
149 Endian::Big => <$t>::from_be_bytes(b),
150 })
151 }
152 )*
153 }
154 impl Cursor<'_> {
155 $(
156 #[doc = concat!("The next `", stringify!($t), "`, in byte order `e`.")]
157 #[inline]
158 pub fn $name(&mut self, e: Endian) -> Result<$t, BoundsError> {
159 let b = self.array()?;
160 Ok(match e {
161 Endian::Little => <$t>::from_le_bytes(b),
162 Endian::Big => <$t>::from_be_bytes(b),
163 })
164 }
165 )*
166 }
167 };
168}
169numbers!(u16: u16, i16: i16, u32: u32, i32: i32, u64: u64, i64: i64, f32: f32, f64: f64);
170
171#[inline]
175pub fn checked_span(
176 offset: usize,
177 count: usize,
178 stride: usize,
179 total: usize,
180) -> Result<Range<usize>, BoundsError> {
181 let overflow = || BoundsError::LengthOverflow {
182 offset,
183 origin: 0,
184 count,
185 stride,
186 };
187 let length = count.checked_mul(stride).ok_or_else(overflow)?;
188 let end = offset.checked_add(length).ok_or_else(overflow)?;
189 if end > total {
190 return Err(BoundsError::OutOfBounds {
191 offset,
192 origin: 0,
193 length,
194 total,
195 });
196 }
197 Ok(offset..end)
198}
199
200#[derive(Clone, Copy, Debug)]
202pub struct View<'a> {
203 data: &'a [u8],
204 origin: usize,
205}
206impl<'a> View<'a> {
207 #[inline]
209 pub fn new(data: &'a [u8]) -> Self {
210 Self::with_origin(data, 0)
211 }
212 #[inline]
214 pub fn with_origin(data: &'a [u8], origin: usize) -> Self {
215 Self { data, origin }
216 }
217 #[inline]
219 fn locate(&self, offset: usize, length: usize) -> Result<Range<usize>, BoundsError> {
220 checked_span(offset, length, 1, self.data.len()).map_err(|e| e.with_origin(self.origin))
221 }
222 #[inline]
224 fn bytes_in(&self, span: Range<usize>) -> Result<&'a [u8], BoundsError> {
225 let (offset, length) = (span.start, span.len());
226 self.data.get(span).ok_or(BoundsError::OutOfBounds {
227 offset,
228 origin: self.origin,
229 length,
230 total: self.data.len(),
231 })
232 }
233 #[inline]
235 pub fn slice(&self, offset: usize, length: usize) -> Result<&'a [u8], BoundsError> {
236 self.bytes_in(self.locate(offset, length)?)
237 }
238 #[inline]
240 pub fn array<const N: usize>(&self, offset: usize) -> Result<[u8; N], BoundsError> {
241 let mut bytes = [0; N];
242 bytes.copy_from_slice(self.slice(offset, N)?);
243 Ok(bytes)
244 }
245 #[inline]
247 pub fn u8(&self, offset: usize) -> Result<u8, BoundsError> {
248 self.array::<1>(offset).map(|[b]| b)
249 }
250 #[inline]
252 pub fn i8(&self, offset: usize) -> Result<i8, BoundsError> {
253 self.array::<1>(offset).map(i8::from_le_bytes)
254 }
255}
256
257#[derive(Clone, Debug)]
260pub struct Cursor<'a> {
261 view: View<'a>,
262 position: usize,
263}
264impl<'a> Cursor<'a> {
265 #[inline]
267 pub fn new(data: &'a [u8]) -> Self {
268 Self::with_origin(data, 0)
269 }
270 #[inline]
272 pub fn with_origin(data: &'a [u8], origin: usize) -> Self {
273 Self {
274 view: View::with_origin(data, origin),
275 position: 0,
276 }
277 }
278 pub fn position(&self) -> usize {
280 self.position
281 }
282 pub fn bytes(&self) -> &'a [u8] {
284 self.view.data
285 }
286 pub fn remaining(&self) -> usize {
288 self.view.data.len().saturating_sub(self.position)
290 }
291 pub fn take(&mut self, length: usize) -> Result<&'a [u8], BoundsError> {
293 let span = self.view.locate(self.position, length)?;
294 let end = span.end;
295 let bytes = self.view.bytes_in(span)?;
296 self.position = end;
297 Ok(bytes)
298 }
299 pub fn take_to(&mut self, end: usize) -> Result<&'a [u8], BoundsError> {
302 let within = View::with_origin(self.view.slice(0, end)?, self.view.origin);
303 let bytes = within.slice(self.position, end.saturating_sub(self.position))?;
304 self.position = end;
305 Ok(bytes)
306 }
307 pub fn seek(&mut self, offset: usize) -> Result<(), BoundsError> {
309 self.view.slice(offset, 0)?;
310 self.position = offset;
311 Ok(())
312 }
313 pub fn array<const N: usize>(&mut self) -> Result<[u8; N], BoundsError> {
315 let span = self.view.locate(self.position, N)?;
316 let end = span.end;
317 let mut bytes = [0; N];
318 bytes.copy_from_slice(self.view.bytes_in(span)?);
319 self.position = end;
320 Ok(bytes)
321 }
322 pub fn u8(&mut self) -> Result<u8, BoundsError> {
324 self.array::<1>().map(|[b]| b)
325 }
326 pub fn i8(&mut self) -> Result<i8, BoundsError> {
328 self.array::<1>().map(i8::from_le_bytes)
329 }
330 pub fn count(&mut self, e: Endian, each: usize) -> Result<usize, BoundsError> {
334 let start = self.position;
335 let n = self.u32(e)?;
336 let n = usize::try_from(n).unwrap_or(usize::MAX);
338 match checked_span(self.position, n, each, self.view.data.len()) {
339 Ok(_) => Ok(n),
340 Err(err) => {
341 self.position = start;
342 Err(err.with_origin(self.view.origin))
343 }
344 }
345 }
346}
347
348#[cfg(test)]
349mod tests {
350 use super::*;
351 #[test]
352 fn spans_and_arithmetic_boundaries() {
353 assert_eq!(checked_span(0, 0, 1, 0), Ok(0..0));
354 assert_eq!(checked_span(4, 0, 7, 4), Ok(4..4));
355 assert!(matches!(
356 checked_span(5, 0, 1, 4),
357 Err(BoundsError::OutOfBounds { .. })
358 ));
359 assert_eq!(checked_span(1, usize::MAX, 0, 1), Ok(1..1));
360 assert!(matches!(
361 checked_span(0, usize::MAX, 2, usize::MAX),
362 Err(BoundsError::LengthOverflow { .. })
363 ));
364 assert!(matches!(
365 checked_span(usize::MAX, 1, 1, usize::MAX),
366 Err(BoundsError::LengthOverflow { .. })
367 ));
368 assert_eq!(View::new(&[1, 2, 3]).array::<2>(1), Ok([2, 3]));
369 assert!(View::new(&[]).array::<1>(0).is_err());
370 }
371 #[test]
372 fn origins_are_exact_even_past_address_space() {
373 let view = View::with_origin(&[], usize::MAX);
374 assert_eq!(view.slice(1, 0).unwrap_err().absolute(), usize::MAX as u128 + 1);
375 assert_eq!(
376 view.slice(1, 0),
377 Err(BoundsError::OutOfBounds {
378 offset: 1,
379 origin: usize::MAX,
380 length: 0,
381 total: 0,
382 })
383 );
384 assert_eq!(
385 view.slice(usize::MAX, 1),
386 Err(BoundsError::LengthOverflow {
387 offset: usize::MAX,
388 origin: usize::MAX,
389 count: 1,
390 stride: 1,
391 })
392 );
393 }
394 #[test]
397 fn numbers_in_either_order_and_named_fields() {
398 let bytes = [0x01, 0x02, 0x03, 0x04, 0x00, 0x00, 0x80, 0x3f];
399 let view = View::with_origin(&bytes, 0x100);
400 assert_eq!(view.u16(0, Endian::Little), Ok(0x0201));
401 assert_eq!(view.u16(0, Endian::Big), Ok(0x0102));
402 assert_eq!(view.u32(0, Endian::Little), Ok(0x0403_0201));
403 assert_eq!(view.i8(2), Ok(3));
404 assert_eq!(view.f32(4, Endian::Little), Ok(1.0));
405 assert_eq!(view.u64(0, Endian::Big), Ok(0x0102_0304_0000_803f));
406 assert!(view.u32(6, Endian::Little).is_err());
407 let mut c = Cursor::with_origin(&bytes, 0x100);
408 assert_eq!(c.u8(), Ok(1));
409 assert_eq!(c.u16(Endian::Big), Ok(0x0203));
410 assert_eq!(c.position(), 3);
411 let err = c.u64(Endian::Little).field("a header's size").unwrap_err();
412 assert_eq!(c.position(), 3);
413 assert_eq!(
414 err.to_string(),
415 "a header's size at 0x103: 8 bytes run past the end of 8"
416 );
417 assert_eq!(c.array::<5>(), Ok([0x04, 0x00, 0x00, 0x80, 0x3f]));
418 let mut c = Cursor::new(&bytes);
420 c.seek(2).unwrap();
421 assert_eq!(c.take_to(4), Ok(&[0x03, 0x04][..]));
422 assert_eq!(c.position(), 4);
423 assert!(c.take_to(3).is_err());
424 assert!(c.take_to(9).is_err());
425 assert_eq!(c.position(), 4);
426 }
427
428 #[test]
431 fn counts_are_held_to_the_rest_of_the_view() {
432 let bytes = [2, 0, 0, 0, 9, 9, 9, 9];
434 let mut c = Cursor::new(&bytes);
435 assert_eq!(c.count(Endian::Little, 2), Ok(2));
436 assert_eq!(c.position(), 4);
437 let mut c = Cursor::with_origin(&bytes, 0x10);
438 assert_eq!(
439 c.count(Endian::Little, 3),
440 Err(BoundsError::OutOfBounds {
441 offset: 4,
442 origin: 0x10,
443 length: 6,
444 total: 8,
445 })
446 );
447 assert_eq!(c.position(), 0);
448 let huge = [0xff, 0xff, 0xff, 0xff];
449 assert!(matches!(
450 Cursor::new(&huge).count(Endian::Little, usize::MAX),
451 Err(BoundsError::LengthOverflow { .. })
452 ));
453 assert!(Cursor::new(&huge[..2]).count(Endian::Little, 1).is_err());
454 }
455
456 #[test]
457 fn failed_cursor_operations_do_not_advance() {
458 let mut cursor = Cursor::with_origin(&[1, 2, 3], 10);
459 assert_eq!(cursor.take(2), Ok(&[1, 2][..]));
460 assert!(cursor.take(2).is_err());
461 assert!(cursor.take(usize::MAX).is_err());
462 assert!(cursor.seek(4).is_err());
463 assert_eq!(cursor.position(), 2);
464 assert_eq!(cursor.remaining(), 1);
465 assert_eq!(cursor.take(1), Ok(&[3][..]));
466 assert_eq!(cursor.take(0), Ok(&[][..]));
467 assert_eq!(cursor.seek(0), Ok(()));
468 assert_eq!(cursor.remaining(), 3);
469 }
470}