Skip to main content

wire_codec/
buf.rs

1//! Zero-copy cursor primitives over borrowed byte slices.
2//!
3//! [`ReadBuf`] wraps an immutable `&[u8]` and tracks a read position. It hands
4//! back sub-slices that borrow from the original buffer, so decoders never copy.
5//! [`WriteBuf`] wraps a mutable `&mut [u8]` and tracks a write position into a
6//! caller-owned region.
7//!
8//! Both cursors are `no_std`-compatible and allocation-free.
9
10use crate::error::{Error, Result};
11
12/// Cursor over an immutable byte slice with a tracked read position.
13///
14/// # Example
15///
16/// ```
17/// use wire_codec::ReadBuf;
18///
19/// let mut buf = ReadBuf::new(&[0xDE, 0xAD, 0xBE, 0xEF]);
20/// assert_eq!(buf.read_u16_be().unwrap(), 0xDEAD);
21/// assert_eq!(buf.read_u16_be().unwrap(), 0xBEEF);
22/// assert_eq!(buf.remaining(), 0);
23/// ```
24#[derive(Debug, Clone)]
25pub struct ReadBuf<'a> {
26    bytes: &'a [u8],
27    pos: usize,
28}
29
30impl<'a> ReadBuf<'a> {
31    /// Wrap `bytes` in a new cursor positioned at the start of the slice.
32    #[inline]
33    pub const fn new(bytes: &'a [u8]) -> Self {
34        Self { bytes, pos: 0 }
35    }
36
37    /// Current read offset within the backing slice.
38    #[inline]
39    pub const fn position(&self) -> usize {
40        self.pos
41    }
42
43    /// Number of bytes still available for reading.
44    #[inline]
45    pub const fn remaining(&self) -> usize {
46        self.bytes.len() - self.pos
47    }
48
49    /// Whether the cursor has consumed every byte of the backing slice.
50    #[inline]
51    pub const fn is_empty(&self) -> bool {
52        self.pos >= self.bytes.len()
53    }
54
55    /// Slice of bytes that have not yet been consumed.
56    #[inline]
57    pub fn remaining_slice(&self) -> &'a [u8] {
58        &self.bytes[self.pos..]
59    }
60
61    /// Look at the next `n` bytes without advancing the cursor.
62    ///
63    /// # Errors
64    ///
65    /// Returns [`Error::UnexpectedEof`] when fewer than `n` bytes remain.
66    #[inline]
67    pub fn peek(&self, n: usize) -> Result<&'a [u8]> {
68        let end = self.pos.checked_add(n).ok_or(Error::UnexpectedEof)?;
69        if end > self.bytes.len() {
70            return Err(Error::UnexpectedEof);
71        }
72        Ok(&self.bytes[self.pos..end])
73    }
74
75    /// Read the next `n` bytes and advance the cursor by `n`.
76    ///
77    /// # Errors
78    ///
79    /// Returns [`Error::UnexpectedEof`] when fewer than `n` bytes remain.
80    #[inline]
81    pub fn read_bytes(&mut self, n: usize) -> Result<&'a [u8]> {
82        let slice = self.peek(n)?;
83        self.pos += n;
84        Ok(slice)
85    }
86
87    /// Advance the cursor by `n` bytes without returning the skipped data.
88    ///
89    /// # Errors
90    ///
91    /// Returns [`Error::UnexpectedEof`] when fewer than `n` bytes remain.
92    #[inline]
93    pub fn advance(&mut self, n: usize) -> Result<()> {
94        let end = self.pos.checked_add(n).ok_or(Error::UnexpectedEof)?;
95        if end > self.bytes.len() {
96            return Err(Error::UnexpectedEof);
97        }
98        self.pos = end;
99        Ok(())
100    }
101
102    /// Read a single byte.
103    ///
104    /// # Errors
105    ///
106    /// Returns [`Error::UnexpectedEof`] when the cursor is exhausted.
107    #[inline]
108    pub fn read_u8(&mut self) -> Result<u8> {
109        if self.pos >= self.bytes.len() {
110            return Err(Error::UnexpectedEof);
111        }
112        let value = self.bytes[self.pos];
113        self.pos += 1;
114        Ok(value)
115    }
116
117    /// Read a big-endian `u16`.
118    ///
119    /// # Errors
120    ///
121    /// Returns [`Error::UnexpectedEof`] when fewer than 2 bytes remain.
122    #[inline]
123    pub fn read_u16_be(&mut self) -> Result<u16> {
124        let bytes = self.read_bytes(2)?;
125        Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
126    }
127
128    /// Read a little-endian `u16`.
129    ///
130    /// # Errors
131    ///
132    /// Returns [`Error::UnexpectedEof`] when fewer than 2 bytes remain.
133    #[inline]
134    pub fn read_u16_le(&mut self) -> Result<u16> {
135        let bytes = self.read_bytes(2)?;
136        Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
137    }
138
139    /// Read a big-endian `u32`.
140    ///
141    /// # Errors
142    ///
143    /// Returns [`Error::UnexpectedEof`] when fewer than 4 bytes remain.
144    #[inline]
145    pub fn read_u32_be(&mut self) -> Result<u32> {
146        let bytes = self.read_bytes(4)?;
147        Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
148    }
149
150    /// Read a little-endian `u32`.
151    ///
152    /// # Errors
153    ///
154    /// Returns [`Error::UnexpectedEof`] when fewer than 4 bytes remain.
155    #[inline]
156    pub fn read_u32_le(&mut self) -> Result<u32> {
157        let bytes = self.read_bytes(4)?;
158        Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
159    }
160
161    /// Read a big-endian `u64`.
162    ///
163    /// # Errors
164    ///
165    /// Returns [`Error::UnexpectedEof`] when fewer than 8 bytes remain.
166    #[inline]
167    pub fn read_u64_be(&mut self) -> Result<u64> {
168        let bytes = self.read_bytes(8)?;
169        let mut arr = [0u8; 8];
170        arr.copy_from_slice(bytes);
171        Ok(u64::from_be_bytes(arr))
172    }
173
174    /// Read a little-endian `u64`.
175    ///
176    /// # Errors
177    ///
178    /// Returns [`Error::UnexpectedEof`] when fewer than 8 bytes remain.
179    #[inline]
180    pub fn read_u64_le(&mut self) -> Result<u64> {
181        let bytes = self.read_bytes(8)?;
182        let mut arr = [0u8; 8];
183        arr.copy_from_slice(bytes);
184        Ok(u64::from_le_bytes(arr))
185    }
186}
187
188/// Cursor over a mutable byte slice with a tracked write position.
189///
190/// # Example
191///
192/// ```
193/// use wire_codec::WriteBuf;
194///
195/// let mut storage = [0u8; 4];
196/// let mut buf = WriteBuf::new(&mut storage);
197/// buf.write_u16_be(0xDEAD).unwrap();
198/// buf.write_u16_be(0xBEEF).unwrap();
199/// assert_eq!(buf.position(), 4);
200/// assert_eq!(&storage, &[0xDE, 0xAD, 0xBE, 0xEF]);
201/// ```
202#[derive(Debug)]
203pub struct WriteBuf<'a> {
204    bytes: &'a mut [u8],
205    pos: usize,
206}
207
208impl<'a> WriteBuf<'a> {
209    /// Wrap `bytes` in a new cursor positioned at offset zero.
210    #[inline]
211    pub fn new(bytes: &'a mut [u8]) -> Self {
212        Self { bytes, pos: 0 }
213    }
214
215    /// Total capacity of the backing slice.
216    #[inline]
217    pub fn capacity(&self) -> usize {
218        self.bytes.len()
219    }
220
221    /// Current write offset within the backing slice.
222    #[inline]
223    pub fn position(&self) -> usize {
224        self.pos
225    }
226
227    /// Number of bytes still available for writing.
228    #[inline]
229    pub fn remaining(&self) -> usize {
230        self.bytes.len() - self.pos
231    }
232
233    /// Borrow the prefix that has been written so far.
234    #[inline]
235    pub fn written(&self) -> &[u8] {
236        &self.bytes[..self.pos]
237    }
238
239    /// Append `src` to the buffer and advance the cursor.
240    ///
241    /// # Errors
242    ///
243    /// Returns [`Error::BufferFull`] if fewer than `src.len()` bytes remain.
244    #[inline]
245    pub fn write_bytes(&mut self, src: &[u8]) -> Result<()> {
246        let end = self.pos.checked_add(src.len()).ok_or(Error::BufferFull)?;
247        if end > self.bytes.len() {
248            return Err(Error::BufferFull);
249        }
250        self.bytes[self.pos..end].copy_from_slice(src);
251        self.pos = end;
252        Ok(())
253    }
254
255    /// Write a single byte.
256    ///
257    /// # Errors
258    ///
259    /// Returns [`Error::BufferFull`] if no capacity remains.
260    #[inline]
261    pub fn write_u8(&mut self, value: u8) -> Result<()> {
262        if self.pos >= self.bytes.len() {
263            return Err(Error::BufferFull);
264        }
265        self.bytes[self.pos] = value;
266        self.pos += 1;
267        Ok(())
268    }
269
270    /// Write a big-endian `u16`.
271    ///
272    /// # Errors
273    ///
274    /// Returns [`Error::BufferFull`] if fewer than 2 bytes remain.
275    #[inline]
276    pub fn write_u16_be(&mut self, value: u16) -> Result<()> {
277        self.write_bytes(&value.to_be_bytes())
278    }
279
280    /// Write a little-endian `u16`.
281    ///
282    /// # Errors
283    ///
284    /// Returns [`Error::BufferFull`] if fewer than 2 bytes remain.
285    #[inline]
286    pub fn write_u16_le(&mut self, value: u16) -> Result<()> {
287        self.write_bytes(&value.to_le_bytes())
288    }
289
290    /// Write a big-endian `u32`.
291    ///
292    /// # Errors
293    ///
294    /// Returns [`Error::BufferFull`] if fewer than 4 bytes remain.
295    #[inline]
296    pub fn write_u32_be(&mut self, value: u32) -> Result<()> {
297        self.write_bytes(&value.to_be_bytes())
298    }
299
300    /// Write a little-endian `u32`.
301    ///
302    /// # Errors
303    ///
304    /// Returns [`Error::BufferFull`] if fewer than 4 bytes remain.
305    #[inline]
306    pub fn write_u32_le(&mut self, value: u32) -> Result<()> {
307        self.write_bytes(&value.to_le_bytes())
308    }
309
310    /// Write a big-endian `u64`.
311    ///
312    /// # Errors
313    ///
314    /// Returns [`Error::BufferFull`] if fewer than 8 bytes remain.
315    #[inline]
316    pub fn write_u64_be(&mut self, value: u64) -> Result<()> {
317        self.write_bytes(&value.to_be_bytes())
318    }
319
320    /// Write a little-endian `u64`.
321    ///
322    /// # Errors
323    ///
324    /// Returns [`Error::BufferFull`] if fewer than 8 bytes remain.
325    #[inline]
326    pub fn write_u64_le(&mut self, value: u64) -> Result<()> {
327        self.write_bytes(&value.to_le_bytes())
328    }
329
330    /// Consume the cursor and return the prefix that has been written.
331    #[inline]
332    pub fn into_written(self) -> &'a mut [u8] {
333        &mut self.bytes[..self.pos]
334    }
335}
336
337#[cfg(test)]
338mod tests {
339    use super::*;
340
341    #[test]
342    fn read_buf_round_trip() {
343        let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
344        let mut buf = ReadBuf::new(&data);
345        assert_eq!(buf.read_u8().unwrap(), 0x01);
346        assert_eq!(buf.read_u16_be().unwrap(), 0x0203);
347        assert_eq!(buf.read_u32_be().unwrap(), 0x04050607);
348        assert_eq!(buf.remaining(), 1);
349        assert_eq!(buf.read_u8().unwrap(), 0x08);
350        assert!(buf.is_empty());
351    }
352
353    #[test]
354    fn read_buf_eof() {
355        let mut buf = ReadBuf::new(&[0x01]);
356        assert_eq!(buf.read_u8().unwrap(), 0x01);
357        assert_eq!(buf.read_u8(), Err(Error::UnexpectedEof));
358    }
359
360    #[test]
361    fn read_buf_peek_does_not_advance() {
362        let buf = ReadBuf::new(&[0xAA, 0xBB]);
363        assert_eq!(buf.peek(2).unwrap(), &[0xAA, 0xBB]);
364        assert_eq!(buf.position(), 0);
365    }
366
367    #[test]
368    fn write_buf_round_trip() {
369        let mut storage = [0u8; 8];
370        let mut buf = WriteBuf::new(&mut storage);
371        buf.write_u8(0x01).unwrap();
372        buf.write_u16_be(0x0203).unwrap();
373        buf.write_u32_le(0x07060504).unwrap();
374        buf.write_u8(0x08).unwrap();
375        assert_eq!(&storage, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
376    }
377
378    #[test]
379    fn write_buf_full() {
380        let mut storage = [0u8; 2];
381        let mut buf = WriteBuf::new(&mut storage);
382        buf.write_u16_be(0x1234).unwrap();
383        assert_eq!(buf.write_u8(0), Err(Error::BufferFull));
384    }
385}