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    #[inline]
119    pub fn read_u16_be(&mut self) -> Result<u16> {
120        let bytes = self.read_bytes(2)?;
121        Ok(u16::from_be_bytes([bytes[0], bytes[1]]))
122    }
123
124    /// Read a little-endian `u16`.
125    #[inline]
126    pub fn read_u16_le(&mut self) -> Result<u16> {
127        let bytes = self.read_bytes(2)?;
128        Ok(u16::from_le_bytes([bytes[0], bytes[1]]))
129    }
130
131    /// Read a big-endian `u32`.
132    #[inline]
133    pub fn read_u32_be(&mut self) -> Result<u32> {
134        let bytes = self.read_bytes(4)?;
135        Ok(u32::from_be_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
136    }
137
138    /// Read a little-endian `u32`.
139    #[inline]
140    pub fn read_u32_le(&mut self) -> Result<u32> {
141        let bytes = self.read_bytes(4)?;
142        Ok(u32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
143    }
144
145    /// Read a big-endian `u64`.
146    #[inline]
147    pub fn read_u64_be(&mut self) -> Result<u64> {
148        let bytes = self.read_bytes(8)?;
149        let mut arr = [0u8; 8];
150        arr.copy_from_slice(bytes);
151        Ok(u64::from_be_bytes(arr))
152    }
153
154    /// Read a little-endian `u64`.
155    #[inline]
156    pub fn read_u64_le(&mut self) -> Result<u64> {
157        let bytes = self.read_bytes(8)?;
158        let mut arr = [0u8; 8];
159        arr.copy_from_slice(bytes);
160        Ok(u64::from_le_bytes(arr))
161    }
162}
163
164/// Cursor over a mutable byte slice with a tracked write position.
165///
166/// # Example
167///
168/// ```
169/// use wire_codec::WriteBuf;
170///
171/// let mut storage = [0u8; 4];
172/// let mut buf = WriteBuf::new(&mut storage);
173/// buf.write_u16_be(0xDEAD).unwrap();
174/// buf.write_u16_be(0xBEEF).unwrap();
175/// assert_eq!(buf.position(), 4);
176/// assert_eq!(&storage, &[0xDE, 0xAD, 0xBE, 0xEF]);
177/// ```
178#[derive(Debug)]
179pub struct WriteBuf<'a> {
180    bytes: &'a mut [u8],
181    pos: usize,
182}
183
184impl<'a> WriteBuf<'a> {
185    /// Wrap `bytes` in a new cursor positioned at offset zero.
186    #[inline]
187    pub fn new(bytes: &'a mut [u8]) -> Self {
188        Self { bytes, pos: 0 }
189    }
190
191    /// Total capacity of the backing slice.
192    #[inline]
193    pub fn capacity(&self) -> usize {
194        self.bytes.len()
195    }
196
197    /// Current write offset within the backing slice.
198    #[inline]
199    pub fn position(&self) -> usize {
200        self.pos
201    }
202
203    /// Number of bytes still available for writing.
204    #[inline]
205    pub fn remaining(&self) -> usize {
206        self.bytes.len() - self.pos
207    }
208
209    /// Borrow the prefix that has been written so far.
210    #[inline]
211    pub fn written(&self) -> &[u8] {
212        &self.bytes[..self.pos]
213    }
214
215    /// Append `src` to the buffer and advance the cursor.
216    ///
217    /// # Errors
218    ///
219    /// Returns [`Error::BufferFull`] if fewer than `src.len()` bytes remain.
220    #[inline]
221    pub fn write_bytes(&mut self, src: &[u8]) -> Result<()> {
222        let end = self.pos.checked_add(src.len()).ok_or(Error::BufferFull)?;
223        if end > self.bytes.len() {
224            return Err(Error::BufferFull);
225        }
226        self.bytes[self.pos..end].copy_from_slice(src);
227        self.pos = end;
228        Ok(())
229    }
230
231    /// Write a single byte.
232    #[inline]
233    pub fn write_u8(&mut self, value: u8) -> Result<()> {
234        if self.pos >= self.bytes.len() {
235            return Err(Error::BufferFull);
236        }
237        self.bytes[self.pos] = value;
238        self.pos += 1;
239        Ok(())
240    }
241
242    /// Write a big-endian `u16`.
243    #[inline]
244    pub fn write_u16_be(&mut self, value: u16) -> Result<()> {
245        self.write_bytes(&value.to_be_bytes())
246    }
247
248    /// Write a little-endian `u16`.
249    #[inline]
250    pub fn write_u16_le(&mut self, value: u16) -> Result<()> {
251        self.write_bytes(&value.to_le_bytes())
252    }
253
254    /// Write a big-endian `u32`.
255    #[inline]
256    pub fn write_u32_be(&mut self, value: u32) -> Result<()> {
257        self.write_bytes(&value.to_be_bytes())
258    }
259
260    /// Write a little-endian `u32`.
261    #[inline]
262    pub fn write_u32_le(&mut self, value: u32) -> Result<()> {
263        self.write_bytes(&value.to_le_bytes())
264    }
265
266    /// Write a big-endian `u64`.
267    #[inline]
268    pub fn write_u64_be(&mut self, value: u64) -> Result<()> {
269        self.write_bytes(&value.to_be_bytes())
270    }
271
272    /// Write a little-endian `u64`.
273    #[inline]
274    pub fn write_u64_le(&mut self, value: u64) -> Result<()> {
275        self.write_bytes(&value.to_le_bytes())
276    }
277
278    /// Consume the cursor and return the prefix that has been written.
279    #[inline]
280    pub fn into_written(self) -> &'a mut [u8] {
281        &mut self.bytes[..self.pos]
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use super::*;
288
289    #[test]
290    fn read_buf_round_trip() {
291        let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
292        let mut buf = ReadBuf::new(&data);
293        assert_eq!(buf.read_u8().unwrap(), 0x01);
294        assert_eq!(buf.read_u16_be().unwrap(), 0x0203);
295        assert_eq!(buf.read_u32_be().unwrap(), 0x04050607);
296        assert_eq!(buf.remaining(), 1);
297        assert_eq!(buf.read_u8().unwrap(), 0x08);
298        assert!(buf.is_empty());
299    }
300
301    #[test]
302    fn read_buf_eof() {
303        let mut buf = ReadBuf::new(&[0x01]);
304        assert_eq!(buf.read_u8().unwrap(), 0x01);
305        assert_eq!(buf.read_u8(), Err(Error::UnexpectedEof));
306    }
307
308    #[test]
309    fn read_buf_peek_does_not_advance() {
310        let buf = ReadBuf::new(&[0xAA, 0xBB]);
311        assert_eq!(buf.peek(2).unwrap(), &[0xAA, 0xBB]);
312        assert_eq!(buf.position(), 0);
313    }
314
315    #[test]
316    fn write_buf_round_trip() {
317        let mut storage = [0u8; 8];
318        let mut buf = WriteBuf::new(&mut storage);
319        buf.write_u8(0x01).unwrap();
320        buf.write_u16_be(0x0203).unwrap();
321        buf.write_u32_le(0x07060504).unwrap();
322        buf.write_u8(0x08).unwrap();
323        assert_eq!(&storage, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
324    }
325
326    #[test]
327    fn write_buf_full() {
328        let mut storage = [0u8; 2];
329        let mut buf = WriteBuf::new(&mut storage);
330        buf.write_u16_be(0x1234).unwrap();
331        assert_eq!(buf.write_u8(0), Err(Error::BufferFull));
332    }
333}