devela/data/access/cursor/byte/define.rs
1// devela/src/data/access/cursor/byte/define.rs
2//
3//! Defines [`ByteCursor`].
4//
5
6#[doc = crate::_tags!(data parser)]
7/// A byte-position cursor over storage `S`.
8#[doc = crate::_doc_meta!{
9 location("data/access/cursor", struct ByteCursor),
10}]
11/// The storage determines the available operations:
12/// - `ByteCursor<&[u8]>` provides read methods.
13/// - `ByteCursor<&mut [u8]>` provides write methods.
14///
15/// The generic parameter is not meant as a broad trait abstraction.
16/// It keeps one public type name while allowing concrete, const-friendly
17/// implementations for shared and mutable byte slices.
18///
19/// This is intentionally not a serializer, deserializer, parser, stream, or I/O
20/// abstraction. It is only `(byte storage, byte position)` plus explicit movement.
21///
22/// # Performance
23/// The fixed-width methods such as `take_4`, `write_4`, `take_u32_le`, and
24/// `write_u32_le` use direct indexed loads/stores after one bounds check.
25/// They are meant to compile close to the current manual binary-codec style.
26///
27/// The borrowed slice methods such as `take` and `peek` do not copy the input.
28/// They return byte slices from the original storage.
29///
30/// The generic copied-array methods such as `take_array::<N>` use a loop.
31/// The dynamic write methods such as `write` and `write_at` also use a loop.
32/// They should not replace [`read_at!`], [`write_at!`], or fixed-width cursor
33/// methods in tiny hot codec headers unless that trade-off is acceptable.
34///
35/// # Example
36/// ```
37/// use devela::{ByteCursor, NotEnoughSpace};
38///
39/// let bytes = b"RIFF\x24\x00\x00\x00WAVE";
40/// let mut cur = ByteCursor::reader(bytes);
41///
42/// assert_eq!(cur.take_4(), Some(*b"RIFF"));
43/// assert_eq!(cur.take_u32_le(), Some(36));
44/// assert_eq!(cur.take_4(), Some(*b"WAVE"));
45/// assert_eq!(cur.pos(), 12);
46///
47/// let mut out = [0u8; 12];
48/// let mut cur = ByteCursor::writer(&mut out);
49///
50/// cur.write_4(*b"RIFF")?;
51/// cur.write_u32_le(36)?;
52/// cur.write_4(*b"WAVE")?;
53///
54/// assert_eq!(&out, b"RIFF\x24\x00\x00\x00WAVE");
55/// # Ok::<(), NotEnoughSpace>(())
56/// ```
57///
58/// [`read_at!`]: crate::read_at
59/// [`write_at!`]: crate::write_at
60///
61/// # Methods
62/// - Common:
63/// - [from_storage_at](#method.from_storage_at).
64/// - [pos](#method.pos).
65/// - [set_pos](#method.set_pos).
66/// - [storage](#method.storage).
67/// - [into_storage](#method.into_storage) ([*copy*](#method.into_storage_copy)).
68///
69/// - Shared for readers & writes:
70/// - [new](#method.new).
71/// - [at](#method.at).
72/// - [as_slice](#method.as_slice).
73/// - [len](#method.len).
74/// - [is_empty](#method.is_empty).
75/// - [remaining_len](#method.remaining_len).
76/// - [try_set_pos](#method.try_set_pos).
77/// - [set_pos_clamped](#method.set_pos_clamped).
78/// - [advance](#method.advance).
79/// - [skip_exact](#method.skip_exact).
80///
81/// - Reader specific:
82/// - [new_read](#method.new_read).
83/// - [is_eof](#method.is_eof).
84/// - [can_take](#method.can_take).
85/// - [rest](#method.rest).
86/// - [peek](#method.peek) ([*_array*](#method.peek_array), [*_u8*](#method.peek_u8),
87/// [*_2*](#method.peek_2), [*_4*](#method.peek_4), [*_8*](#method.peek_8)).
88/// - [take](#method.take) ([*_array*](#method.take_array), [*_u8*](#method.take_u8),
89/// [*_2*](#method.take_2), [*_4*](#method.take_4), [*_8*](#method.take_8),
90/// [*_u16_le*](#method.take_u16_le), [*_u16_be*](#method.take_u16_be),
91/// [*_u32_le*](#method.take_u32_le), [*_u32_be*](#method.take_u32_be),
92/// [*_u64_le*](#method.take_u64_le), [*_u64_be*](#method.take_u64_be)).
93///
94/// - Writer specific:
95/// - [new_write](#method.new_write).
96/// - [as_mut_slice](#method.as_mut_slice).
97/// - [can_write](#method.can_write) ([*at*](#method.can_write_at)).
98/// - [write](#method.write) ([*_u8*](#method.write_u8),
99/// [*_2*](#method.write_2), [*_4*](#method.write_4), [*_8*](#method.write_8),
100/// [*_u16_le*](#method.write_u16_le), [*_u16_be*](#method.write_u16_be),
101/// [*_u32_le*](#method.write_u32_le), [*_u32_be*](#method.write_u32_be),
102/// [*_u64_le*](#method.write_u64_le), [*_u64_be*](#method.write_u64_be)).
103/// - [write_at](#method_write_at) ([*_u8*](#method.write_at_u8), [*_2*](#method.write_at_2),
104/// [*_4*](#method.write_at_4), [*_8*](#method.write_at_8),
105/// [*_u32_le*](#method.write_at_u32_le), [*_u32_be*](#method.write_at_u32_be)).
106#[derive(Clone, Copy, Debug, PartialEq, Eq)]
107pub struct ByteCursor<S> {
108 pub(super) storage: S,
109 pub(super) pos: usize,
110}
111
112/// # Common methods
113impl<S> ByteCursor<S> {
114 /// Creates a cursor from `storage` and `pos`.
115 ///
116 /// This does not validate `pos`, because generic storage has no length
117 /// contract. Slice-specific constructors and methods perform bounds checks
118 /// where needed.
119 #[must_use]
120 pub const fn from_storage_at(storage: S, pos: usize) -> Self {
121 Self { storage, pos }
122 }
123 /// Returns the current byte position.
124 #[must_use]
125 pub const fn pos(&self) -> usize {
126 self.pos
127 }
128 /// Sets the current byte position without validating it.
129 ///
130 /// Slice-specific read/write methods will fail if the position is out of
131 /// bounds. Use this only when the position was computed by trusted format
132 /// logic.
133 pub const fn set_pos(&mut self, pos: usize) {
134 self.pos = pos;
135 }
136 /// Returns a shared reference to the underlying storage.
137 #[must_use]
138 pub const fn storage(&self) -> &S {
139 &self.storage
140 }
141 /// Consumes the cursor and returns its storage.
142 #[must_use]
143 pub fn into_storage(self) -> S {
144 self.storage
145 }
146 /// Consumes the cursor and returns its storage.
147 #[must_use]
148 pub const fn into_storage_copy(self) -> S
149 where
150 S: Copy,
151 {
152 self.storage
153 }
154}