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. Every fallible
9//! method is atomic: on error the position is unchanged and, for
10//! [`WriteBuf`], no byte of the backing slice is modified.
11
12use crate::error::{Error, Result};
13
14/// Generate fixed-width `ReadBuf` readers from `from_*_bytes`.
15macro_rules! read_fixed {
16    ($($(#[$attr:meta])* $name:ident -> $ty:ty, $conv:ident, $desc:literal, $size:literal;)*) => {$(
17        #[doc = concat!("Read a ", $desc, ".")]
18        ///
19        /// # Errors
20        ///
21        #[doc = concat!("Returns [`Error::UnexpectedEof`] when fewer than ", $size, " bytes remain.")]
22        $(#[$attr])*
23        #[inline]
24        pub fn $name(&mut self) -> Result<$ty> {
25            self.read_array().map(<$ty>::$conv)
26        }
27    )*};
28}
29
30/// Generate fixed-width `WriteBuf` writers from `to_*_bytes`.
31macro_rules! write_fixed {
32    ($($(#[$attr:meta])* $name:ident($ty:ty), $conv:ident, $desc:literal, $size:literal;)*) => {$(
33        #[doc = concat!("Write a ", $desc, ".")]
34        ///
35        /// # Errors
36        ///
37        #[doc = concat!("Returns [`Error::BufferFull`] if fewer than ", $size, " bytes remain.")]
38        $(#[$attr])*
39        #[inline]
40        pub fn $name(&mut self, value: $ty) -> Result<()> {
41            self.write_bytes(&value.$conv())
42        }
43    )*};
44}
45
46/// Cursor over an immutable byte slice with a tracked read position.
47///
48/// # Example
49///
50/// ```
51/// use wire_codec::ReadBuf;
52///
53/// let mut buf = ReadBuf::new(&[0xDE, 0xAD, 0xBE, 0xEF]);
54/// assert_eq!(buf.read_u16_be().unwrap(), 0xDEAD);
55/// assert_eq!(buf.read_u16_be().unwrap(), 0xBEEF);
56/// assert_eq!(buf.remaining(), 0);
57/// ```
58#[derive(Debug, Clone)]
59pub struct ReadBuf<'a> {
60    bytes: &'a [u8],
61    pos: usize,
62}
63
64impl<'a> ReadBuf<'a> {
65    /// Wrap `bytes` in a new cursor positioned at the start of the slice.
66    #[inline]
67    #[must_use]
68    pub const fn new(bytes: &'a [u8]) -> Self {
69        Self { bytes, pos: 0 }
70    }
71
72    /// Current read offset within the backing slice.
73    ///
74    /// Because every read is atomic, after an error this is the offset of the
75    /// value that failed to decode, which makes it useful in diagnostics.
76    #[inline]
77    #[must_use]
78    pub const fn position(&self) -> usize {
79        self.pos
80    }
81
82    /// Number of bytes still available for reading.
83    #[inline]
84    #[must_use]
85    pub const fn remaining(&self) -> usize {
86        self.bytes.len() - self.pos
87    }
88
89    /// Whether the cursor has consumed every byte of the backing slice.
90    #[inline]
91    #[must_use]
92    pub const fn is_empty(&self) -> bool {
93        self.pos >= self.bytes.len()
94    }
95
96    /// Slice of bytes that have not yet been consumed.
97    #[inline]
98    #[must_use]
99    pub fn remaining_slice(&self) -> &'a [u8] {
100        &self.bytes[self.pos..]
101    }
102
103    /// Look at the next `n` bytes without advancing the cursor.
104    ///
105    /// # Errors
106    ///
107    /// Returns [`Error::UnexpectedEof`] when fewer than `n` bytes remain.
108    #[inline]
109    pub fn peek(&self, n: usize) -> Result<&'a [u8]> {
110        let end = self.pos.checked_add(n).ok_or(Error::UnexpectedEof)?;
111        self.bytes.get(self.pos..end).ok_or(Error::UnexpectedEof)
112    }
113
114    /// Look at the next byte without advancing the cursor.
115    ///
116    /// # Errors
117    ///
118    /// Returns [`Error::UnexpectedEof`] when the cursor is exhausted.
119    ///
120    /// # Example
121    ///
122    /// ```
123    /// use wire_codec::ReadBuf;
124    ///
125    /// let buf = ReadBuf::new(&[0x7F]);
126    /// assert_eq!(buf.peek_u8().unwrap(), 0x7F);
127    /// assert_eq!(buf.position(), 0);
128    /// ```
129    #[inline]
130    pub fn peek_u8(&self) -> Result<u8> {
131        self.bytes
132            .get(self.pos)
133            .copied()
134            .ok_or(Error::UnexpectedEof)
135    }
136
137    /// Read the next `n` bytes and advance the cursor by `n`.
138    ///
139    /// # Errors
140    ///
141    /// Returns [`Error::UnexpectedEof`] when fewer than `n` bytes remain.
142    #[inline]
143    pub fn read_bytes(&mut self, n: usize) -> Result<&'a [u8]> {
144        let slice = self.peek(n)?;
145        self.pos += n;
146        Ok(slice)
147    }
148
149    /// Read the next `N` bytes as a fixed-size array (copied).
150    ///
151    /// # Errors
152    ///
153    /// Returns [`Error::UnexpectedEof`] when fewer than `N` bytes remain.
154    ///
155    /// # Example
156    ///
157    /// ```
158    /// use wire_codec::ReadBuf;
159    ///
160    /// let mut buf = ReadBuf::new(b"GIF89a...");
161    /// let magic: [u8; 6] = buf.read_array().unwrap();
162    /// assert_eq!(&magic, b"GIF89a");
163    /// ```
164    #[inline]
165    pub fn read_array<const N: usize>(&mut self) -> Result<[u8; N]> {
166        self.read_array_ref().copied()
167    }
168
169    /// Borrow the next `N` bytes as a fixed-size array reference (zero-copy).
170    ///
171    /// # Errors
172    ///
173    /// Returns [`Error::UnexpectedEof`] when fewer than `N` bytes remain.
174    ///
175    /// # Example
176    ///
177    /// ```
178    /// use wire_codec::ReadBuf;
179    ///
180    /// let data = [1, 2, 3, 4, 5];
181    /// let mut buf = ReadBuf::new(&data);
182    /// let head: &[u8; 4] = buf.read_array_ref().unwrap();
183    /// assert_eq!(head, &[1, 2, 3, 4]);
184    /// ```
185    #[inline]
186    pub fn read_array_ref<const N: usize>(&mut self) -> Result<&'a [u8; N]> {
187        let array = self
188            .remaining_slice()
189            .first_chunk::<N>()
190            .ok_or(Error::UnexpectedEof)?;
191        self.pos += N;
192        Ok(array)
193    }
194
195    /// Split off the next `n` bytes as an independent cursor and advance past
196    /// them.
197    ///
198    /// Useful for a length-delimited sub-record: decode it from the returned
199    /// cursor, then check [`ReadBuf::is_empty`] to make sure it was consumed
200    /// exactly.
201    ///
202    /// # Errors
203    ///
204    /// Returns [`Error::UnexpectedEof`] when fewer than `n` bytes remain.
205    ///
206    /// # Example
207    ///
208    /// ```
209    /// use wire_codec::ReadBuf;
210    ///
211    /// let mut buf = ReadBuf::new(&[0x02, 0xAA, 0xBB, 0xCC]);
212    /// let len = usize::from(buf.read_u8().unwrap());
213    /// let mut record = buf.split_to(len).unwrap();
214    /// assert_eq!(record.read_u16_be().unwrap(), 0xAABB);
215    /// assert!(record.is_empty());
216    /// assert_eq!(buf.remaining_slice(), &[0xCC]);
217    /// ```
218    #[inline]
219    pub fn split_to(&mut self, n: usize) -> Result<ReadBuf<'a>> {
220        self.read_bytes(n).map(ReadBuf::new)
221    }
222
223    /// Advance the cursor by `n` bytes without returning the skipped data.
224    ///
225    /// # Errors
226    ///
227    /// Returns [`Error::UnexpectedEof`] when fewer than `n` bytes remain.
228    #[inline]
229    pub fn advance(&mut self, n: usize) -> Result<()> {
230        self.read_bytes(n).map(|_| ())
231    }
232
233    /// Advance past `n` bytes that the caller has already validated.
234    ///
235    /// Crate-internal fast path for decoders that parse from
236    /// [`ReadBuf::remaining_slice`] and commit only on success.
237    #[inline]
238    pub(crate) fn consume(&mut self, n: usize) {
239        debug_assert!(n <= self.remaining());
240        self.pos += n;
241    }
242
243    /// Read a single byte.
244    ///
245    /// # Errors
246    ///
247    /// Returns [`Error::UnexpectedEof`] when the cursor is exhausted.
248    #[inline]
249    pub fn read_u8(&mut self) -> Result<u8> {
250        let value = self.peek_u8()?;
251        self.pos += 1;
252        Ok(value)
253    }
254
255    /// Read a single byte as an `i8` (two's complement).
256    ///
257    /// # Errors
258    ///
259    /// Returns [`Error::UnexpectedEof`] when the cursor is exhausted.
260    #[inline]
261    pub fn read_i8(&mut self) -> Result<i8> {
262        self.read_u8().map(|b| b as i8)
263    }
264
265    read_fixed! {
266        read_u16_be -> u16, from_be_bytes, "big-endian `u16`", "2";
267        read_u16_le -> u16, from_le_bytes, "little-endian `u16`", "2";
268        read_u32_be -> u32, from_be_bytes, "big-endian `u32`", "4";
269        read_u32_le -> u32, from_le_bytes, "little-endian `u32`", "4";
270        read_u64_be -> u64, from_be_bytes, "big-endian `u64`", "8";
271        read_u64_le -> u64, from_le_bytes, "little-endian `u64`", "8";
272        read_u128_be -> u128, from_be_bytes, "big-endian `u128`", "16";
273        read_u128_le -> u128, from_le_bytes, "little-endian `u128`", "16";
274        read_i16_be -> i16, from_be_bytes, "big-endian `i16`", "2";
275        read_i16_le -> i16, from_le_bytes, "little-endian `i16`", "2";
276        read_i32_be -> i32, from_be_bytes, "big-endian `i32`", "4";
277        read_i32_le -> i32, from_le_bytes, "little-endian `i32`", "4";
278        read_i64_be -> i64, from_be_bytes, "big-endian `i64`", "8";
279        read_i64_le -> i64, from_le_bytes, "little-endian `i64`", "8";
280        read_i128_be -> i128, from_be_bytes, "big-endian `i128`", "16";
281        read_i128_le -> i128, from_le_bytes, "little-endian `i128`", "16";
282        read_f32_be -> f32, from_be_bytes, "big-endian IEEE 754 `f32`", "4";
283        read_f32_le -> f32, from_le_bytes, "little-endian IEEE 754 `f32`", "4";
284        read_f64_be -> f64, from_be_bytes, "big-endian IEEE 754 `f64`", "8";
285        read_f64_le -> f64, from_le_bytes, "little-endian IEEE 754 `f64`", "8";
286    }
287}
288
289/// Cursor over a mutable byte slice with a tracked write position.
290///
291/// # Example
292///
293/// ```
294/// use wire_codec::WriteBuf;
295///
296/// let mut storage = [0u8; 4];
297/// let mut buf = WriteBuf::new(&mut storage);
298/// buf.write_u16_be(0xDEAD).unwrap();
299/// buf.write_u16_be(0xBEEF).unwrap();
300/// assert_eq!(buf.position(), 4);
301/// assert_eq!(&storage, &[0xDE, 0xAD, 0xBE, 0xEF]);
302/// ```
303///
304/// While a `WriteBuf` is alive it mutably borrows the storage, so read the
305/// encoded bytes back through [`WriteBuf::written`] (or capture
306/// [`WriteBuf::position`] in a local first) rather than indexing the storage
307/// directly:
308///
309/// ```
310/// use wire_codec::WriteBuf;
311///
312/// let mut storage = [0u8; 8];
313/// let mut buf = WriteBuf::new(&mut storage);
314/// buf.write_u8(1).unwrap();
315/// let bytes: &[u8] = buf.written();
316/// assert_eq!(bytes, &[1]);
317/// ```
318#[derive(Debug)]
319pub struct WriteBuf<'a> {
320    bytes: &'a mut [u8],
321    pos: usize,
322}
323
324impl<'a> WriteBuf<'a> {
325    /// Wrap `bytes` in a new cursor positioned at offset zero.
326    #[inline]
327    #[must_use]
328    pub fn new(bytes: &'a mut [u8]) -> Self {
329        Self { bytes, pos: 0 }
330    }
331
332    /// Total capacity of the backing slice.
333    #[inline]
334    #[must_use]
335    pub fn capacity(&self) -> usize {
336        self.bytes.len()
337    }
338
339    /// Current write offset within the backing slice.
340    #[inline]
341    #[must_use]
342    pub fn position(&self) -> usize {
343        self.pos
344    }
345
346    /// Number of bytes still available for writing.
347    #[inline]
348    #[must_use]
349    pub fn remaining(&self) -> usize {
350        self.bytes.len() - self.pos
351    }
352
353    /// Borrow the prefix that has been written so far.
354    #[inline]
355    #[must_use]
356    pub fn written(&self) -> &[u8] {
357        &self.bytes[..self.pos]
358    }
359
360    /// Append `src` to the buffer and advance the cursor.
361    ///
362    /// # Errors
363    ///
364    /// Returns [`Error::BufferFull`] if fewer than `src.len()` bytes remain.
365    #[inline]
366    pub fn write_bytes(&mut self, src: &[u8]) -> Result<()> {
367        let end = self.pos.checked_add(src.len()).ok_or(Error::BufferFull)?;
368        let dst = self.bytes.get_mut(self.pos..end).ok_or(Error::BufferFull)?;
369        dst.copy_from_slice(src);
370        self.pos = end;
371        Ok(())
372    }
373
374    /// Write a single byte.
375    ///
376    /// # Errors
377    ///
378    /// Returns [`Error::BufferFull`] if no capacity remains.
379    #[inline]
380    pub fn write_u8(&mut self, value: u8) -> Result<()> {
381        let slot = self.bytes.get_mut(self.pos).ok_or(Error::BufferFull)?;
382        *slot = value;
383        self.pos += 1;
384        Ok(())
385    }
386
387    /// Write a single `i8` (two's complement).
388    ///
389    /// # Errors
390    ///
391    /// Returns [`Error::BufferFull`] if no capacity remains.
392    #[inline]
393    pub fn write_i8(&mut self, value: i8) -> Result<()> {
394        self.write_u8(value as u8)
395    }
396
397    write_fixed! {
398        write_u16_be(u16), to_be_bytes, "big-endian `u16`", "2";
399        write_u16_le(u16), to_le_bytes, "little-endian `u16`", "2";
400        write_u32_be(u32), to_be_bytes, "big-endian `u32`", "4";
401        write_u32_le(u32), to_le_bytes, "little-endian `u32`", "4";
402        write_u64_be(u64), to_be_bytes, "big-endian `u64`", "8";
403        write_u64_le(u64), to_le_bytes, "little-endian `u64`", "8";
404        write_u128_be(u128), to_be_bytes, "big-endian `u128`", "16";
405        write_u128_le(u128), to_le_bytes, "little-endian `u128`", "16";
406        write_i16_be(i16), to_be_bytes, "big-endian `i16`", "2";
407        write_i16_le(i16), to_le_bytes, "little-endian `i16`", "2";
408        write_i32_be(i32), to_be_bytes, "big-endian `i32`", "4";
409        write_i32_le(i32), to_le_bytes, "little-endian `i32`", "4";
410        write_i64_be(i64), to_be_bytes, "big-endian `i64`", "8";
411        write_i64_le(i64), to_le_bytes, "little-endian `i64`", "8";
412        write_i128_be(i128), to_be_bytes, "big-endian `i128`", "16";
413        write_i128_le(i128), to_le_bytes, "little-endian `i128`", "16";
414        write_f32_be(f32), to_be_bytes, "big-endian IEEE 754 `f32`", "4";
415        write_f32_le(f32), to_le_bytes, "little-endian IEEE 754 `f32`", "4";
416        write_f64_be(f64), to_be_bytes, "big-endian IEEE 754 `f64`", "8";
417        write_f64_le(f64), to_le_bytes, "little-endian IEEE 754 `f64`", "8";
418    }
419
420    /// Mutable view of the unwritten tail of the buffer.
421    ///
422    /// Crate-internal: encoders write directly into this slice, then call
423    /// [`WriteBuf::commit`] with the number of bytes they produced.
424    #[inline]
425    pub(crate) fn spare_mut(&mut self) -> &mut [u8] {
426        &mut self.bytes[self.pos..]
427    }
428
429    /// Mark `n` bytes of [`WriteBuf::spare_mut`] as written.
430    #[inline]
431    pub(crate) fn commit(&mut self, n: usize) {
432        debug_assert!(n <= self.remaining());
433        self.pos += n;
434    }
435
436    /// Roll the write position back to `pos`, discarding everything written
437    /// after it. Used to keep multi-step writes atomic on error.
438    #[inline]
439    pub(crate) fn rewind(&mut self, pos: usize) {
440        debug_assert!(pos <= self.pos);
441        self.pos = pos;
442    }
443
444    /// Consume the cursor and return the prefix that has been written.
445    #[inline]
446    #[must_use]
447    pub fn into_written(self) -> &'a mut [u8] {
448        &mut self.bytes[..self.pos]
449    }
450}
451
452#[cfg(test)]
453mod tests {
454    use super::*;
455
456    #[test]
457    fn read_buf_round_trip() {
458        let data = [0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08];
459        let mut buf = ReadBuf::new(&data);
460        assert_eq!(buf.read_u8().unwrap(), 0x01);
461        assert_eq!(buf.read_u16_be().unwrap(), 0x0203);
462        assert_eq!(buf.read_u32_be().unwrap(), 0x04050607);
463        assert_eq!(buf.remaining(), 1);
464        assert_eq!(buf.read_u8().unwrap(), 0x08);
465        assert!(buf.is_empty());
466    }
467
468    #[test]
469    fn read_buf_eof() {
470        let mut buf = ReadBuf::new(&[0x01]);
471        assert_eq!(buf.read_u8().unwrap(), 0x01);
472        assert_eq!(buf.read_u8(), Err(Error::UnexpectedEof));
473        assert_eq!(buf.read_i8(), Err(Error::UnexpectedEof));
474        assert_eq!(buf.peek_u8(), Err(Error::UnexpectedEof));
475    }
476
477    #[test]
478    fn read_buf_peek_does_not_advance() {
479        let buf = ReadBuf::new(&[0xAA, 0xBB]);
480        assert_eq!(buf.peek(2).unwrap(), &[0xAA, 0xBB]);
481        assert_eq!(buf.position(), 0);
482        assert_eq!(buf.peek(3), Err(Error::UnexpectedEof));
483        assert_eq!(buf.peek(usize::MAX), Err(Error::UnexpectedEof));
484    }
485
486    #[test]
487    fn every_fixed_width_read_in_both_orders() {
488        let data: Vec<u8> = (1..=16).collect();
489        let mut be = ReadBuf::new(&data);
490        assert_eq!(
491            be.read_u128_be().unwrap(),
492            u128::from_be_bytes(data[..].try_into().unwrap())
493        );
494        let mut le = ReadBuf::new(&data);
495        assert_eq!(
496            le.read_u128_le().unwrap(),
497            u128::from_le_bytes(data[..].try_into().unwrap())
498        );
499
500        let mut r = ReadBuf::new(&data);
501        assert_eq!(r.read_u16_le().unwrap(), 0x0201);
502        assert_eq!(r.read_u32_le().unwrap(), 0x0605_0403);
503        assert_eq!(r.read_u64_le().unwrap(), 0x0E0D_0C0B_0A09_0807);
504        assert_eq!(r.read_u16_be().unwrap(), 0x0F10);
505
506        let mut r = ReadBuf::new(&data);
507        assert_eq!(r.read_u64_be().unwrap(), 0x0102_0304_0506_0708);
508
509        let neg = [0xFFu8; 16];
510        let mut r = ReadBuf::new(&neg);
511        assert_eq!(r.read_i8().unwrap(), -1);
512        assert_eq!(r.read_i16_be().unwrap(), -1);
513        assert_eq!(r.read_i16_le().unwrap(), -1);
514        assert_eq!(r.read_i32_be().unwrap(), -1);
515        assert_eq!(r.read_i32_le().unwrap(), -1);
516        assert_eq!(r.remaining(), 3);
517        let mut r = ReadBuf::new(&neg);
518        assert_eq!(r.read_i64_be().unwrap(), -1);
519        assert_eq!(r.read_i64_le().unwrap(), -1);
520        let mut r = ReadBuf::new(&neg);
521        assert_eq!(r.read_i128_be().unwrap(), -1);
522        let mut r = ReadBuf::new(&neg);
523        assert_eq!(r.read_i128_le().unwrap(), -1);
524    }
525
526    #[test]
527    fn float_round_trip() {
528        let mut storage = [0u8; 24];
529        let mut w = WriteBuf::new(&mut storage);
530        w.write_f32_be(1.5).unwrap();
531        w.write_f32_le(-2.25).unwrap();
532        w.write_f64_be(core::f64::consts::PI).unwrap();
533        w.write_f64_le(f64::MIN_POSITIVE).unwrap();
534        assert_eq!(&storage[..4], &1.5f32.to_be_bytes());
535        let mut r = ReadBuf::new(&storage);
536        assert_eq!(r.read_f32_be().unwrap(), 1.5);
537        assert_eq!(r.read_f32_le().unwrap(), -2.25);
538        assert_eq!(r.read_f64_be().unwrap(), core::f64::consts::PI);
539        assert_eq!(r.read_f64_le().unwrap(), f64::MIN_POSITIVE);
540        assert!(r.is_empty());
541    }
542
543    #[test]
544    fn short_fixed_reads_are_atomic() {
545        let mut r = ReadBuf::new(&[1, 2, 3]);
546        assert_eq!(r.read_u32_be(), Err(Error::UnexpectedEof));
547        assert_eq!(r.read_array::<4>(), Err(Error::UnexpectedEof));
548        assert_eq!(r.read_array_ref::<4>(), Err(Error::UnexpectedEof));
549        assert_eq!(
550            r.split_to(4).map(|b| b.remaining()),
551            Err(Error::UnexpectedEof)
552        );
553        assert_eq!(r.advance(4), Err(Error::UnexpectedEof));
554        assert_eq!(r.advance(usize::MAX), Err(Error::UnexpectedEof));
555        assert_eq!(r.position(), 0);
556    }
557
558    #[test]
559    fn arrays_split_and_advance() {
560        let data = [1u8, 2, 3, 4, 5, 6];
561        let mut r = ReadBuf::new(&data);
562        assert_eq!(r.read_array::<2>().unwrap(), [1, 2]);
563        assert_eq!(r.read_array_ref::<1>().unwrap(), &[3]);
564        r.advance(1).unwrap();
565        assert_eq!(r.position(), 4);
566        assert_eq!(r.remaining_slice(), &[5, 6]);
567        let sub = r.split_to(2).unwrap();
568        assert_eq!(sub.remaining_slice(), &[5, 6]);
569        assert!(r.is_empty());
570        assert_eq!(r.read_array::<0>().unwrap(), [0u8; 0]);
571    }
572
573    #[test]
574    fn write_buf_round_trip() {
575        let mut storage = [0u8; 8];
576        let mut buf = WriteBuf::new(&mut storage);
577        buf.write_u8(0x01).unwrap();
578        buf.write_u16_be(0x0203).unwrap();
579        buf.write_u32_le(0x07060504).unwrap();
580        buf.write_u8(0x08).unwrap();
581        assert_eq!(&storage, &[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]);
582    }
583
584    #[test]
585    fn every_fixed_width_write() {
586        let mut storage = [0u8; 64];
587        let mut w = WriteBuf::new(&mut storage);
588        w.write_i8(-2).unwrap();
589        w.write_u16_le(0x0102).unwrap();
590        w.write_i16_be(-2).unwrap();
591        w.write_i16_le(-2).unwrap();
592        w.write_u32_be(0x0102_0304).unwrap();
593        w.write_i32_be(-2).unwrap();
594        w.write_i32_le(-2).unwrap();
595        w.write_u64_be(1).unwrap();
596        w.write_u64_le(1).unwrap();
597        w.write_i64_be(-2).unwrap();
598        w.write_i64_le(-2).unwrap();
599        assert_eq!(w.position(), 1 + 2 * 3 + 4 * 3 + 8 * 4);
600        let n = w.position();
601        let mut r = ReadBuf::new(&storage[..n]);
602        assert_eq!(r.read_i8().unwrap(), -2);
603        assert_eq!(r.read_u16_le().unwrap(), 0x0102);
604        assert_eq!(r.read_i16_be().unwrap(), -2);
605        assert_eq!(r.read_i16_le().unwrap(), -2);
606        assert_eq!(r.read_u32_be().unwrap(), 0x0102_0304);
607        assert_eq!(r.read_i32_be().unwrap(), -2);
608        assert_eq!(r.read_i32_le().unwrap(), -2);
609        assert_eq!(r.read_u64_be().unwrap(), 1);
610        assert_eq!(r.read_u64_le().unwrap(), 1);
611        assert_eq!(r.read_i64_be().unwrap(), -2);
612        assert_eq!(r.read_i64_le().unwrap(), -2);
613
614        let mut storage = [0u8; 64];
615        let mut w = WriteBuf::new(&mut storage);
616        w.write_u128_be(7).unwrap();
617        w.write_u128_le(7).unwrap();
618        w.write_i128_be(-7).unwrap();
619        w.write_i128_le(-7).unwrap();
620        let mut r = ReadBuf::new(&storage);
621        assert_eq!(r.read_u128_be().unwrap(), 7);
622        assert_eq!(r.read_u128_le().unwrap(), 7);
623        assert_eq!(r.read_i128_be().unwrap(), -7);
624        assert_eq!(r.read_i128_le().unwrap(), -7);
625    }
626
627    #[test]
628    fn write_buf_full() {
629        let mut storage = [0u8; 2];
630        let mut buf = WriteBuf::new(&mut storage);
631        buf.write_u16_be(0x1234).unwrap();
632        assert_eq!(buf.write_u8(0), Err(Error::BufferFull));
633        assert_eq!(buf.write_i8(0), Err(Error::BufferFull));
634        assert_eq!(buf.write_bytes(&[]), Ok(()));
635        assert_eq!(buf.write_bytes(&[0]), Err(Error::BufferFull));
636    }
637
638    #[test]
639    fn write_buf_accessors() {
640        let mut storage = [0u8; 4];
641        let mut buf = WriteBuf::new(&mut storage);
642        assert_eq!(buf.capacity(), 4);
643        assert_eq!(buf.remaining(), 4);
644        buf.write_bytes(&[9, 8, 7, 6]).unwrap();
645        assert_eq!(buf.remaining(), 0);
646        assert_eq!(buf.write_u32_be(0), Err(Error::BufferFull));
647        assert_eq!(buf.position(), 4);
648        let written = buf.into_written();
649        written[0] = 1;
650        assert_eq!(storage, [1, 8, 7, 6]);
651    }
652}