1#![warn(clippy::indexing_slicing, clippy::arithmetic_side_effects)]
4
5use super::error::SourceLocation;
6use super::probe::{ParseError, ParseErrorKind};
7use super::space::SpaceId;
8use crate::bytes::{
9 assemble_f32_be, assemble_f32_le, assemble_f64_be, assemble_f64_le, assemble_u16_be,
10 assemble_u16_le, assemble_u24_be, assemble_u24_le, assemble_u32_be, assemble_u32_le,
11 assemble_u64_be, assemble_u64_le,
12};
13
14pub fn bounded_len(count: u64, element_size: usize, remaining: usize) -> Option<usize> {
21 if element_size == 0 {
22 return None;
23 }
24 let count = usize::try_from(count).ok()?;
25 let bytes = count.checked_mul(element_size)?;
26 (bytes <= remaining).then_some(count)
27}
28
29const _: () = assert!(
32 usize::BITS >= 32,
33 "a target whose usize is narrower than 32 bits cannot hold a u32 index"
34);
35
36pub const fn index_from_u32(value: u32) -> usize {
43 #[cfg(target_pointer_width = "32")]
44 {
45 usize::from_ne_bytes(value.to_ne_bytes())
46 }
47 #[cfg(target_pointer_width = "64")]
48 {
49 let bytes = value.to_ne_bytes();
50 #[cfg(target_endian = "little")]
51 {
52 usize::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3], 0, 0, 0, 0])
53 }
54 #[cfg(target_endian = "big")]
55 {
56 usize::from_ne_bytes([0, 0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3]])
57 }
58 }
59}
60
61const _: () = assert!(
64 usize::BITS <= 64,
65 "a target whose usize is wider than 64 bits cannot hold every index in a u64"
66);
67
68pub const fn u64_from_index(value: usize) -> u64 {
77 #[cfg(target_pointer_width = "64")]
78 {
79 u64::from_ne_bytes(value.to_ne_bytes())
80 }
81 #[cfg(target_pointer_width = "32")]
82 {
83 let bytes = value.to_ne_bytes();
84 #[cfg(target_endian = "little")]
85 {
86 u64::from_ne_bytes([bytes[0], bytes[1], bytes[2], bytes[3], 0, 0, 0, 0])
87 }
88 #[cfg(target_endian = "big")]
89 {
90 u64::from_ne_bytes([0, 0, 0, 0, bytes[0], bytes[1], bytes[2], bytes[3]])
91 }
92 }
93}
94
95#[must_use]
106pub fn index_from_u64(offset: u64) -> Option<usize> {
107 usize::try_from(offset).ok()
108}
109
110#[must_use]
129pub fn id_from_index(index: usize) -> Option<u32> {
130 u32::try_from(index).ok()
131}
132
133#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub struct BoundedCount(usize);
136
137impl BoundedCount {
138 pub fn get(self) -> usize {
140 self.0
141 }
142}
143
144#[derive(Debug, Clone, Copy)]
163pub struct View<'a> {
164 window: &'a [u8],
165 unread: &'a [u8],
166 space: SpaceId,
167 start: usize,
168 end: usize,
169}
170
171impl<'a> View<'a> {
172 pub(super) fn over_space(bytes: &'a [u8], space: SpaceId) -> View<'a> {
174 View {
175 window: bytes,
176 unread: bytes,
177 space,
178 start: 0,
179 end: bytes.len(),
180 }
181 }
182
183 pub fn over_retained(bytes: &'a [u8]) -> View<'a> {
189 Self::over_space(bytes, SpaceId::ROOT)
190 }
191
192 pub(super) fn space(self) -> SpaceId {
194 self.space
195 }
196
197 pub fn position(self) -> usize {
199 self.end.abs_diff(self.remaining())
203 }
204
205 pub fn read_len(self) -> usize {
211 self.window.len().abs_diff(self.unread.len())
212 }
213
214 pub fn start(self) -> usize {
216 self.start
217 }
218
219 pub fn end(self) -> usize {
221 self.end
222 }
223
224 fn offset_of(self, position: usize) -> Option<usize> {
230 position.checked_sub(self.start)
231 }
232
233 pub fn unread(self) -> &'a [u8] {
237 self.unread
238 }
239
240 pub fn remaining(self) -> usize {
242 self.unread.len()
243 }
244
245 pub fn is_empty(self) -> bool {
247 self.unread.is_empty()
248 }
249
250 pub fn location(self) -> SourceLocation {
252 SourceLocation {
253 space: self.space,
254 offset: u64_from_index(self.position()),
255 }
256 }
257
258 pub fn location_at(self, offset: usize) -> SourceLocation {
260 SourceLocation {
261 space: self.space,
262 offset: u64_from_index(offset),
263 }
264 }
265
266 pub fn window(self) -> &'a [u8] {
268 self.window
269 }
270
271 pub fn take(&mut self, count: usize) -> Option<&'a [u8]> {
277 let (bytes, unread) = self.unread.split_at_checked(count)?;
278 self.unread = unread;
279 Some(bytes)
280 }
281
282 pub fn array<const N: usize>(&mut self) -> Option<[u8; N]> {
284 self.take(N)?.try_into().ok()
285 }
286
287 pub fn skip(&mut self, count: usize) -> Option<()> {
289 self.take(count).map(|_| ())
290 }
291
292 pub fn seek(&mut self, position: usize) -> Option<()> {
297 self.unread = self.window.get(self.offset_of(position)?..)?;
298 Some(())
299 }
300
301 pub fn seek_to_end(&mut self) {
313 self.unread = self.window.split_at(self.window.len()).1;
314 }
315
316 pub fn u8(&mut self) -> Option<u8> {
318 self.array::<1>().map(|[value]| value)
319 }
320
321 pub fn child(self, start: usize, end: usize) -> Option<View<'a>> {
327 let window = self
328 .window
329 .get(self.offset_of(start)?..self.offset_of(end)?)?;
330 Some(View {
331 window,
332 unread: window,
333 space: self.space,
334 start,
335 end,
336 })
337 }
338
339 fn take_child(&mut self, count: usize) -> Option<View<'a>> {
351 let start = self.position();
352 let window = self.take(count)?;
353 Some(View {
354 window,
355 unread: window,
356 space: self.space,
357 start,
358 end: self.position(),
359 })
360 }
361
362 pub fn counted(self, count: u64, min_element_size: usize) -> Option<BoundedCount> {
364 bounded_len(count, min_element_size, self.remaining()).map(BoundedCount)
365 }
366
367 fn eof(self, needed: u64) -> ParseError {
369 ParseError {
370 location: self.location(),
371 kind: ParseErrorKind::UnexpectedEof { needed },
372 operation: "required_read",
373 }
374 }
375
376 pub fn req_take(&mut self, count: usize) -> Result<&'a [u8], ParseError> {
378 match self.take(count) {
379 Some(bytes) => Ok(bytes),
380 None => Err(self.eof(u64_from_index(count))),
381 }
382 }
383
384 pub fn req_take_child(&mut self, count: usize) -> Result<View<'a>, ParseError> {
392 match self.take_child(count) {
393 Some(view) => Ok(view),
394 None => Err(self.eof(u64_from_index(count))),
395 }
396 }
397
398 pub fn req_u8(&mut self) -> Result<u8, ParseError> {
400 match self.u8() {
401 Some(value) => Ok(value),
402 None => Err(self.eof(1)),
403 }
404 }
405}
406
407const fn decode_i16_le(bytes: [u8; 2]) -> i16 {
408 i16::from_le_bytes(bytes)
410}
411
412const fn decode_i32_le(bytes: [u8; 4]) -> i32 {
413 i32::from_le_bytes(bytes)
415}
416
417const fn decode_i64_le(bytes: [u8; 8]) -> i64 {
418 i64::from_le_bytes(bytes)
420}
421
422const fn decode_i16_be(bytes: [u8; 2]) -> i16 {
423 i16::from_be_bytes(bytes)
425}
426
427macro_rules! view_readers {
428 ($(($probe:ident, [$($req:ident)?], $at:ident, $ty:ty, $decode:ident, $size:literal)),* $(,)?) => {
429 impl View<'_> {
430 $(
431 #[doc = concat!("Probe read of a `", stringify!($ty), "` in the serialized byte order.")]
432 pub fn $probe(&mut self) -> Option<$ty> {
433 self.array::<$size>().map($decode)
434 }
435
436 $(
437 #[doc = concat!("Required-read mirror of [`View::", stringify!($probe), "`].")]
438 pub fn $req(&mut self) -> Result<$ty, ParseError> {
439 match self.array::<$size>() {
440 Some(bytes) => Ok($decode(bytes)),
441 None => Err(self.eof($size)),
442 }
443 }
444 )?
445
446 #[doc = concat!(
447 "Offset probe of a `",
448 stringify!($ty),
449 "` over retained bytes. Sequential readers should keep a live `View` and call [`View::",
450 stringify!($probe),
451 "`] instead."
452 )]
453 pub fn $at(bytes: &[u8], offset: usize) -> Option<$ty> {
454 let mut view = View::over_retained(bytes);
455 view.seek(offset)?;
456 view.$probe()
457 }
458 )*
459 }
460 };
461}
462
463view_readers!(
464 (u16_le, [req_u16_le], u16_le_at, u16, assemble_u16_le, 2),
465 (i16_le, [req_i16_le], i16_le_at, i16, decode_i16_le, 2),
466 (u32_le, [req_u32_le], u32_le_at, u32, assemble_u32_le, 4),
467 (i32_le, [req_i32_le], i32_le_at, i32, decode_i32_le, 4),
468 (u64_le, [req_u64_le], u64_le_at, u64, assemble_u64_le, 8),
469 (i64_le, [req_i64_le], i64_le_at, i64, decode_i64_le, 8),
470 (f32_le, [req_f32_le], f32_le_at, f32, assemble_f32_le, 4),
471 (f64_le, [req_f64_le], f64_le_at, f64, assemble_f64_le, 8),
472 (u16_be, [], u16_be_at, u16, assemble_u16_be, 2),
473 (i16_be, [], i16_be_at, i16, decode_i16_be, 2),
474 (u32_be, [req_u32_be], u32_be_at, u32, assemble_u32_be, 4),
475 (u64_be, [], u64_be_at, u64, assemble_u64_be, 8),
476 (f32_be, [req_f32_be], f32_be_at, f32, assemble_f32_be, 4),
477 (f64_be, [], f64_be_at, f64, assemble_f64_be, 8),
478);
479
480impl View<'_> {
481 pub fn u24_le_at(bytes: &[u8], offset: usize) -> Option<u32> {
483 let end = offset.checked_add(3)?;
484 let bytes = bytes.get(offset..end)?.try_into().ok()?;
485 Some(assemble_u24_le(bytes))
486 }
487
488 pub fn u24_be_at(bytes: &[u8], offset: usize) -> Option<u32> {
490 let end = offset.checked_add(3)?;
491 let bytes = bytes.get(offset..end)?.try_into().ok()?;
492 Some(assemble_u24_be(bytes))
493 }
494}
495
496#[cfg(test)]
497mod tests {
498 use super::{
499 bounded_len, id_from_index, index_from_u32, index_from_u64, u64_from_index, BoundedCount,
500 ParseError, ParseErrorKind, SourceLocation, View,
501 };
502 use crate::decode::space::SpaceId;
503
504 #[test]
505 #[cfg(target_pointer_width = "64")]
506 fn an_in_memory_position_past_the_id_width_states_no_identifier() {
507 assert_eq!(id_from_index(0), Some(0));
508 assert_eq!(id_from_index(index_from_u32(u32::MAX)), Some(u32::MAX));
509 assert_eq!(id_from_index(index_from_u32(u32::MAX) + 1), None);
510 }
511
512 #[test]
515 #[cfg(target_pointer_width = "64")]
516 fn an_offset_past_the_index_width_states_no_index() {
517 assert_eq!(index_from_u64(0), Some(0));
518 assert_eq!(index_from_u64(u64_from_index(usize::MAX)), Some(usize::MAX));
519 assert_eq!(
520 index_from_u64(u64::from(u32::MAX)),
521 Some(index_from_u32(u32::MAX))
522 );
523 }
524
525 #[test]
526 fn a_u32_index_widens_to_the_same_value() {
527 assert_eq!(index_from_u32(0), 0);
528 assert_eq!(
529 index_from_u32(u32::MAX),
530 usize::try_from(u32::MAX).expect("u32 fits usize")
531 );
532 }
533
534 #[test]
537 fn an_index_widens_to_the_same_value_and_back() {
538 assert_eq!(u64_from_index(0), 0);
539 assert_eq!(u64_from_index(1), 1);
540 let widest = u64_from_index(usize::MAX);
541 assert_eq!(widest, u64::try_from(usize::MAX).expect("usize fits u64"));
542 assert_eq!(usize::try_from(widest), Ok(usize::MAX));
543 }
544
545 #[test]
546 fn counted_reads_only_plausible_lengths() {
547 let payload = [1u8, 0, 0, 0, 2, 0, 0, 0];
548 let mut view = View::over_space(&payload, SpaceId::ROOT);
549 assert_eq!(view.counted(2, 4).map(BoundedCount::get), Some(2));
550 let mut values = [0; 2];
551 for value in &mut values {
552 *value = view.u32_le().expect("two fixture values");
553 }
554 assert_eq!(values, [1, 2]);
555 let view = View::over_space(&payload, SpaceId::ROOT);
556 assert_eq!(view.counted(u64::from(u32::MAX), 4), None);
557 }
558
559 #[test]
560 fn counted_rejects_impossible_counts() {
561 let payload = [0u8; 40];
562 let view = View::over_space(&payload, SpaceId::ROOT);
563 assert_eq!(view.counted(10, 4).map(BoundedCount::get), Some(10));
564 assert_eq!(view.counted(11, 4), None);
565 assert_eq!(bounded_len(u64::MAX, 20, usize::MAX), None);
566 }
567
568 #[test]
571 fn the_window_is_the_slice_the_constructor_proved() {
572 let payload = [1u8, 2, 3, 4, 5, 6, 7, 8];
573 let view = View::over_space(&payload, SpaceId::ROOT);
574 assert_eq!(view.window(), payload.as_slice());
575 assert_eq!(view.start(), 0);
576 assert_eq!(view.end(), payload.len());
577 let mut child = view.child(2, 6).expect("contained child");
578 assert_eq!(child.window(), payload.get(2..6).expect("fixture range"));
579 assert_eq!(child.start(), 2);
580 assert_eq!(child.end(), 6);
581 assert_eq!(child.position(), 2);
582 assert_eq!(child.remaining(), 4);
583 assert_eq!(child.take(2), payload.get(2..4));
584 assert_eq!(child.position(), 4);
585 assert_eq!(child.remaining(), 2);
586 assert_eq!(child.window(), payload.get(2..6).expect("fixture range"));
587 assert_eq!(child.take(3), None);
588 assert_eq!(child.position(), 4);
589 let inner = view.child(2, 6).expect("contained child");
590 assert!(inner.child(1, 6).is_none(), "lower bound under the window");
591 assert!(inner.child(2, 7).is_none(), "upper bound past the window");
592 assert!(inner.child(5, 3).is_none(), "inverted range");
593 assert_eq!(inner.child(3, 5).map(View::window), payload.get(3..5));
594 }
595
596 #[test]
599 fn the_unread_bytes_are_the_window_suffix_the_cursor_has_not_passed() {
600 let payload = [1u8, 2, 3, 4, 5, 6, 7, 8];
601 let mut view = View::over_space(&payload, SpaceId::ROOT)
602 .child(2, 7)
603 .expect("contained child");
604 assert_eq!(view.unread(), payload.get(2..7).expect("fixture range"));
605 assert_eq!(view.read_len(), 0);
606 assert_eq!(view.remaining(), 5);
607 assert_eq!(view.position(), 2);
608 assert_eq!(view.take(3), payload.get(2..5));
609 assert_eq!(view.unread(), payload.get(5..7).expect("fixture range"));
610 assert_eq!(view.read_len(), 3);
611 assert_eq!(view.remaining(), 2);
612 assert_eq!(view.position(), 5);
613 assert_eq!(view.take(3), None, "a refused take states no bytes");
614 assert_eq!(view.unread(), payload.get(5..7).expect("fixture range"));
615 assert_eq!(
616 view.position(),
617 5,
618 "a refused take does not move the cursor"
619 );
620 assert_eq!(view.seek(7), Some(()));
621 assert!(view.unread().is_empty());
622 assert!(view.is_empty());
623 assert_eq!(view.read_len(), 5);
624 assert_eq!(view.position(), 7);
625 assert_eq!(view.seek(2), Some(()));
626 assert_eq!(view.read_len(), 0);
627 assert_eq!(view.remaining(), 5);
628 assert_eq!(view.window(), payload.get(2..7).expect("fixture range"));
629 }
630
631 fn bounds(view: View<'_>) -> (usize, usize, usize, usize) {
634 (view.start(), view.end(), view.position(), view.remaining())
635 }
636
637 #[test]
640 fn take_child_states_the_taken_range_as_a_window() {
641 let payload = [1u8, 2, 3, 4, 5, 6, 7, 8];
642 let mut view = View::over_space(&payload, SpaceId::ROOT);
643 assert_eq!(view.seek(2), Some(()));
644 assert_eq!(view.take_child(3).map(View::window), payload.get(2..5));
645 assert_eq!(view.position(), 5, "the take advanced by its own count");
646 assert_eq!(view.remaining(), 3);
647 assert_eq!(view.seek(2), Some(()));
648 assert_eq!(
649 view.take_child(3).map(bounds),
650 Some((2, 5, 2, 3)),
651 "the window is the taken range and its cursor is at the start"
652 );
653 assert_eq!(
654 view.take_child(3).map(|window| window.location().offset),
655 Some(u64_from_index(5)),
656 "the result's location is the position the take began at"
657 );
658 assert_eq!(view.position(), 8);
659 assert_eq!(view.take_child(0).map(bounds), Some((8, 8, 8, 0)));
660 assert_eq!(view.take_child(0).map(View::window), payload.get(8..8));
661 assert_eq!(view.position(), 8, "a zero-length take does not advance");
662 assert_eq!(
663 view.req_take_child(0).map(bounds),
664 Ok((8, 8, 8, 0)),
665 "the required mirror takes the empty window at the end"
666 );
667 }
668
669 #[test]
672 fn a_taken_window_carves_its_own_children() {
673 let payload = [1u8, 2, 3, 4, 5, 6, 7, 8];
674 let mut view = View::over_space(&payload, SpaceId::ROOT);
675 assert_eq!(view.skip(1), Some(()));
676 let outer = view.take_child(6);
677 assert_eq!(outer.map(bounds), Some((1, 7, 1, 6)));
678 assert_eq!(
679 outer.map(|window| window.child(3, 6).map(bounds)),
680 Some(Some((3, 6, 3, 3))),
681 "a child of a taken window states the space's own bounds"
682 );
683 assert_eq!(
684 outer.map(|mut window| {
685 window.skip(2)?;
686 window.take_child(3).map(View::window)
687 }),
688 Some(payload.get(3..6)),
689 "a taken window of a taken window is the same bytes"
690 );
691 assert_eq!(
692 outer.map(|window| window.child(0, 6).map(bounds)),
693 Some(None),
694 "a lower bound under the taken window is refused"
695 );
696 }
697
698 #[test]
701 fn a_take_child_past_the_window_leaves_the_view_unmoved() {
702 let payload = [0u8; 8];
703 let mut view = View::over_space(&payload, SpaceId::ROOT);
704 assert_eq!(view.seek(6), Some(()));
705 assert_eq!(view.take_child(3).map(bounds), None);
706 assert_eq!(
707 view.position(),
708 6,
709 "a refused take does not move the cursor"
710 );
711 assert_eq!(view.remaining(), 2);
712 assert_eq!(
713 view.req_take_child(3).map(bounds),
714 Err(ParseError {
715 location: SourceLocation {
716 space: SpaceId::ROOT,
717 offset: 6,
718 },
719 kind: ParseErrorKind::UnexpectedEof { needed: 3 },
720 operation: "required_read",
721 })
722 );
723 assert_eq!(view.position(), 6);
724 assert_eq!(
725 view.req_take_child(3).map(bounds).err(),
726 view.req_take(3).err(),
727 "the same located truncation the required take states"
728 );
729 assert_eq!(view.take_child(2).map(bounds), Some((6, 8, 6, 2)));
730 }
731
732 #[test]
733 fn seek_honors_lower_bound() {
734 let payload = [0u8; 8];
735 let mut view = View::over_space(&payload, SpaceId::ROOT)
736 .child(2, 6)
737 .expect("valid child");
738 assert_eq!(view.seek(0), None);
739 assert_eq!(view.seek(2), Some(()));
740 assert_eq!(view.seek(6), Some(()));
741 assert_eq!(view.seek(7), None);
742 }
743
744 #[test]
745 fn seek_to_end_lands_on_the_window_bound_with_nothing_unread() {
746 let payload = [0u8; 8];
747 let mut view = View::over_space(&payload, SpaceId::ROOT)
748 .child(2, 6)
749 .expect("valid child");
750 assert_eq!(view.u8(), Some(0));
751 view.seek_to_end();
752 assert_eq!(view.position(), view.end());
753 assert_eq!(view.remaining(), 0);
754 assert!(view.is_empty());
755 assert_eq!(view.read_len(), view.window().len());
756 assert!(view.unread().is_empty());
757 }
758
759 #[test]
760 fn offset_helpers_match_live_reads() {
761 let mut bytes = Vec::new();
762 bytes.extend_from_slice(&0x0102_u16.to_le_bytes());
763 bytes.extend_from_slice(&0x0304_u16.to_be_bytes());
764 bytes.extend_from_slice(&(-5_i16).to_be_bytes());
765 bytes.extend_from_slice(&1.5_f32.to_le_bytes());
766 assert_eq!(View::u16_le_at(&bytes, 0), Some(0x0102));
767 assert_eq!(View::u16_be_at(&bytes, 2), Some(0x0304));
768 assert_eq!(View::i16_be_at(&bytes, 4), Some(-5));
769 assert_eq!(View::f32_le_at(&bytes, 6), Some(1.5));
770 assert_eq!(View::u16_le_at(&bytes, bytes.len()), None);
771 assert_eq!(View::u24_le_at(&[0x02, 0x01, 0x00], 0), Some(0x0102));
772 assert_eq!(View::u24_be_at(&[0x01, 0x02, 0x03], 0), Some(0x0001_0203));
773 assert_eq!(View::u24_le_at(&[0, 1], 0), None);
774 let mut view = View::over_space(&bytes, SpaceId::ROOT);
775 assert_eq!(view.u16_le(), Some(0x0102));
776 assert_eq!(view.u16_be(), Some(0x0304));
777 assert_eq!(view.i16_be(), Some(-5));
778 assert_eq!(view.f32_le(), Some(1.5));
779 }
780}