wire_rs/
lib.rs

1// Copyright (c) 2022 Nathaniel Bennett
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9//! Extensible interface for converting data to/from wire protocols.
10//!
11//! The purpose of this crate is to provide a simplified interface for reading from/writing to a
12//! buffer of bytes in an efficient and memory-safe way. It provides default implementations for
13//! reading/writing primitive types--integers (u8, i32, f64, etc), strings (&str), byte slices
14//! and arrays (&[u8; 3]). It also offers extensibilty to other types and more complex structs
15//! via traits ([`WireRead`], [`WireWrite`], and their variants).
16//!
17//! In addition to supporting standard single-slice buffers, this crate provides equivalent
18//! support for non-contiguous buffers by means of the [`VectoredReader`] and [`VectoredWriter`],
19//! along with corresponding [`VectoredRead`] and [`VectoredWrite`] traits and their variants.
20//! Instead of operating on a single slice (`&[u8]`), these operate on a slice of slices
21//! (`&[&[u8]]`) and transparently handle reading from/writing to a chunk that spans multiple
22//! slices. This can be useful in cases where it is not desirable or feasible to guarantee
23//! contiguous memory--ring buffers, two-dimensional arrays, `iovec` API calls,
24//! `PACKET_MMAP` buffers and the like.
25//!
26//! The Reader and Writer data structures exposed by this crate are effectively light-weight
27//! wrappers around slices, meaning that they can be created, copied and dropped freely with
28//! very little memory overhead.
29//!
30//! ## Examples
31//!
32//! To read data from a simple slice:
33//!
34//! ```rust
35//! use wire_rs::{WireError, WireReader};
36//!
37//! fn read_data<'a>(slice: &'a [u8]) -> Result<(u16, i8, &'a str), WireError> {
38//!     let mut reader: WireReader = WireReader::new(slice);
39//!     let s1 = reader.read()?;
40//!     let s2 = reader.read()?;
41//!     let s3 = reader.read_ref(7)?;
42//!     Ok((s1, s2, s3))
43//! }
44//!
45//! ```
46//!
47//! To write data to a slice:
48//!
49//! ```rust
50//! use wire_rs::{WireError, WireWriter};
51//!
52//! fn write_data(slice: &mut [u8]) -> Result<(), WireError> {
53//!     let mut writer: WireWriter = WireWriter::new(slice);
54//!     writer.write(&10i32)?;
55//!     writer.write_part::<u64, 3>(&0xFFu64)?; // Write the least significant 3 bytes of the given value to the wire (as long as the value will fit)
56//!     writer.write("Hello, world!")?;
57//!     writer.finalize()?; // Return an error if there were more bytes available on `slice` that we didn't write to
58//!     Ok(())
59//! }
60//!
61//! ```
62//!
63//! To read/write data in little endian:
64//!
65//! ```rust
66//! use wire_rs::{WireReader, WireWriter};
67//!
68//! fn endianness() {
69//!     let default_reader: WireReader = WireReader::new(&[]);
70//!     // ^ Big-endian by default (same for WireWriter)
71//!     // Note: you may need to explicitly specify the `WireReader` type
72//!     // such as in this case to use the default.
73//!
74//!     let be_reader = WireReader::<true>::new(&[]);
75//!     // Explicitly set to big-endian ^
76//!
77//!     let le_writer = WireWriter::<false>::new(&mut []);
78//!     // Explicitly set to little-endian ^
79//! }
80//!
81//! ```
82
83#![cfg_attr(not(feature = "std"), no_std)]
84
85mod reader;
86mod writer;
87
88pub use reader::{
89    VectoredCursor, VectoredRead, VectoredReadComp, VectoredReadPart, VectoredReadRef,
90    VectoredReader, WireCursor, WireRead, WireReadComp, WireReadPart, WireReadRef, WireReader,
91};
92
93pub use writer::{
94    VectoredBufMut, VectoredCursorMut, VectoredWrite, VectoredWritePart, VectoredWriter,
95    WireCursorMut, WireWrite, WireWritePart, WireWriter,
96};
97
98use core::convert;
99use reader::*;
100use writer::*;
101
102/// An error in reading from or writing to a wire.
103///
104/// New error types may be added to this enum, so it should not be exhaustively matched against.
105#[non_exhaustive]
106#[derive(Clone, Copy, PartialEq, Eq, Debug)]
107pub enum WireError {
108    /// Returned when a Reader or Writer contains too few remaining bytes to fully read or write
109    /// the desired type.
110    InsufficientBytes,
111    /// Returned when some bytes remain on the wire after the final data type is read or written.
112    ///
113    /// The `finalize()` or `finalize_after()` methods can be used to check that all of the slice
114    /// passed into a Reader or Writer is used. This is particularly useful for wire protocols
115    /// that include a length field at the beginning of a packet that needs to be validated.
116    /// When bytes remain in a Reader or Writer and one of the above methods is called, the
117    /// `InsufficientBytes` error will be returned.
118    ExtraBytes,
119    /// Returned when the type being read requires a particular format that the bytes on the
120    /// wire do not adhere to, or when the type being written is not within a certain range of
121    /// values that can be serialized.
122    ///
123    /// The latter case can only occur for types that implement either the [`WireWritePart`]
124    /// trait or the [`VectoredWritePart`] trait. For example, the following would return an
125    /// `InvalidData` error:
126    ///
127    /// ```rust
128    /// use wire_rs::{WireError, WireWriter};
129    ///
130    /// fn decode_partial_out_of_range() -> Result<(), WireError> {
131    ///     let mut buf = [0u8; 4];
132    ///     let out_of_range = 0x0100u16;
133    ///     let mut writer: WireWriter = WireWriter::new(buf.as_mut_slice());
134    ///     writer.write_part::<u16, 1>(&out_of_range) // Returns Err(WireError::InvalidData)
135    /// }
136    /// ```
137    ///
138    /// Whereas a number within the range of values that can be encoded for the given size
139    /// would return Ok(()):
140    ///
141    /// ```rust
142    /// use wire_rs::{WireError, WireWriter};
143    ///
144    /// fn decode_partial_within_range() -> Result<(), WireError> {
145    ///     let mut buf = [0u8; 4];
146    ///     let within_range = 0xFFu64;
147    ///     let mut writer: WireWriter = WireWriter::new(buf.as_mut_slice());
148    ///     writer.write_part::<u64, 1>(&within_range)
149    ///     // Returns Ok(())
150    /// }
151    /// ```
152    ///
153    /// As an example of the former case, a Reader would return an error when invalid UTF-8
154    /// is read in for a `str` type, such as:
155    ///
156    /// ```rust
157    /// use wire_rs::{WireError, WireReader};
158    ///
159    /// fn decode_bad_utf8() -> Result<(), WireError> {
160    ///     let buf = [0xC3, 0x28]; // Invalid 2-octet UTF-8 sequence
161    ///     let mut reader: WireReader = WireReader::new(buf.as_slice());
162    ///     let s: &str = reader.read_remaining()?;
163    ///     // Returns Err(WireError::InvalidData)
164    ///     return Ok(())
165    /// }
166    /// ```
167    InvalidData(&'static str),
168    /// An internal error in the Reader or Writer.
169    ///
170    /// This will not be raised unless there is some bug in the implementation of the Reader of
171    /// Writer, most likely caused by an invariant not holding. If encountered, this error should
172    /// be counted as a fatal error in (de/)serializing data from the wire, and the Reader or
173    /// Writer that returned this error should not be used for subsequent operations.
174    Internal,
175}
176
177/// An index of a buffer or vectored buffers.
178///
179/// This type is created from a Wire or Vectored Reader/Writer type.
180/// Its primary use is to provide a mechanism for advancing a slice or vectored slice
181/// up by the amount consumed by the Reader/Writer without causing any lifetime issues.
182///
183/// Readers and Writers carry a reference to the buffer they are reading,
184/// which means that no mutations can be performed on the referenced buffer while a Reader is
185/// in scope. Converting a reader into a `WireIndex` provides a mechanism to drop the Reader
186/// while retaining the index that the Reader had reached. Once the buffer has been mutated
187/// (e.g. additional data being written into it), a slice of the new contents (starting at the
188/// index stored in the `WireIndex`) can be used to create a new Reader. That reader can then
189/// continue to extract data from the buffer.
190pub struct WireIndex {
191    /// The index of which buffer in the set of vectored buffers the reader or writer was at.
192    ///
193    /// If the given `WireIndex` was created from a [`WireReader`] or [`WireWriter`] and not a
194    /// [`VectoredReader`] or [`VectoredWriter`], this value will always be set to 0.
195    pub vectored_idx: usize,
196    /// The index within the buffer that the reader or writer was at.
197    pub slice_idx: usize,
198}
199
200impl convert::From<WireReader<'_>> for WireIndex {
201    fn from(reader: WireReader<'_>) -> Self {
202        WireIndex {
203            vectored_idx: 0,
204            slice_idx: _internal_wirereader_consumed(&reader),
205        }
206    }
207}
208
209impl convert::From<VectoredReader<'_>> for WireIndex {
210    fn from(reader: VectoredReader<'_>) -> Self {
211        WireIndex {
212            vectored_idx: _internal_vectoredreader_vec_index(&reader),
213            slice_idx: _internal_vectoredreader_slice_index(&reader),
214        }
215    }
216}
217
218impl convert::From<WireWriter<'_>> for WireIndex {
219    fn from(writer: WireWriter<'_>) -> Self {
220        WireIndex {
221            vectored_idx: 0,
222            slice_idx: _internal_wirewriter_consumed(&writer),
223        }
224    }
225}
226
227impl convert::From<VectoredWriter<'_>> for WireIndex {
228    fn from(writer: VectoredWriter<'_>) -> Self {
229        WireIndex {
230            vectored_idx: _internal_vectoredwriter_vec_index(&writer),
231            slice_idx: _internal_vectoredwriter_slice_index(&writer),
232        }
233    }
234}
235
236#[cfg(test)]
237mod tests {
238    use super::*;
239
240    const BUF1_LEN: usize = 16;
241    const BUF1: [u8; BUF1_LEN] = [
242        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
243        0x0f,
244    ];
245
246    #[cfg(not(feature = "ioslice"))]
247    #[test]
248    fn vectored_final_index() {
249        let iovec1: [&[u8]; 3] = [&BUF1[BUF1_LEN..], &BUF1[..11], &[]];
250
251        let mut r1 = VectoredReader::new(&iovec1);
252        assert!(r1.read() == Ok(0x0001020304050607u64));
253        assert!(r1.read() == Ok(0x0809i16));
254        assert!(r1.read() == Ok(0x0au8));
255        let i1 = WireIndex::from(r1);
256        assert!(i1.vectored_idx == 1);
257        assert!(i1.slice_idx == 11);
258
259        let mut r1 = VectoredReader::new(&iovec1);
260        assert!(r1.read_remaining() == Ok(&BUF1[..11]));
261        let i1 = WireIndex::from(r1);
262        assert!(i1.vectored_idx == 1);
263        assert!(i1.slice_idx == 11);
264    }
265
266    #[cfg(not(feature = "ioslice"))]
267    #[test]
268    fn vectored_empty_index() {
269        let iovec1: [&[u8]; 6] = [&[], &[], &BUF1[..4], &[], &BUF1[4..8], &[]];
270        let mut r1 = VectoredReader::new(&iovec1);
271        let i1 = WireIndex::from(r1);
272        assert!(i1.vectored_idx == 0);
273        assert!(i1.slice_idx == 0);
274        assert!(r1.read::<i128>().is_err());
275        let i2 = WireIndex::from(r1);
276        assert!(i2.vectored_idx == 0);
277        assert!(i2.slice_idx == 0);
278        assert!(r1.read::<i32>() == Ok(0x00010203i32));
279        let i3 = WireIndex::from(r1);
280        assert!(i3.vectored_idx == 2);
281        assert!(i3.slice_idx == 4);
282
283        assert!(r1.read::<i32>() == Ok(0x04050607i32));
284        assert!(r1.finalize().is_ok());
285        let i4 = WireIndex::from(r1);
286
287        assert!(i4.vectored_idx == 4);
288        assert!(i4.slice_idx == 4);
289    }
290
291    #[cfg(not(feature = "ioslice"))]
292    #[test]
293    fn vectored_wraparound_empty() {
294        let iovec1: [&[u8]; 2] = [&BUF1[BUF1_LEN..], &BUF1[..11]];
295
296        let mut r1 = VectoredReader::new(&iovec1);
297        assert!(r1.read::<u128>().is_err());
298        let i1 = WireIndex::from(r1);
299        assert!(i1.vectored_idx == 0);
300        assert!(i1.slice_idx == 0);
301
302        let mut r1 = VectoredReader::new(&iovec1);
303        assert!(r1.read::<u8>().is_ok());
304        let i1 = WireIndex::from(r1);
305        assert!(i1.vectored_idx == 1);
306        assert!(i1.slice_idx == 1);
307
308        let iovec2: [&[u8]; 2] = [&BUF1[BUF1_LEN - 4..], &BUF1[..7]];
309
310        let mut r2 = VectoredReader::new(&iovec2);
311        assert!(r2.read::<u128>().is_err());
312        let i2 = WireIndex::from(r2);
313        assert!(i2.vectored_idx == 0);
314        assert!(i2.slice_idx == 0);
315
316        let mut r2 = VectoredReader::new(&iovec2);
317        assert!(r2.read::<u32>().is_ok());
318        let i2 = WireIndex::from(r2);
319        assert!(i2.vectored_idx == 0);
320        assert!(i2.slice_idx == 4);
321
322        let mut r2 = VectoredReader::new(&iovec2);
323        assert!(r2.read::<u8>().is_ok());
324        assert!(r2.read::<u32>().is_ok());
325        let i2 = WireIndex::from(r2);
326        assert!(i2.vectored_idx == 1);
327        assert!(i2.slice_idx == 1);
328    }
329
330    #[test]
331    fn simple_read_finalize() {
332        let bytes = [0x12, 0x34, 0x56, 0x78];
333        let mut r1: WireReader = WireReader::new(&bytes);
334
335        let val1 = r1.read();
336        let val2 = r1.read();
337        let val3 = WireReader::finalize_after(r1.read(), &r1);
338        //        let i1 = WireIndex::from(r1);
339
340        assert!(val1 == Ok(0x12u8));
341        assert!(val2 == Ok(0x34u8));
342        assert!(val3 == Ok(0x5678u16));
343    }
344
345    #[test]
346    fn read_finalize_insufficient_bytes() {
347        let bytes = [0x12, 0x34, 0x56, 0x78];
348        let mut r1 = WireReader::<true>::new(&bytes);
349
350        let val1 = r1.read();
351        let val2 = r1.read();
352        let val3: Result<u32, WireError> = WireReader::finalize_after(r1.read(), &r1);
353
354        assert!(val1 == Ok(0x12u8));
355        assert!(val2 == Ok(0x34u8));
356        assert!(val3 == Err(WireError::InsufficientBytes));
357    }
358
359    #[test]
360    fn read_str() {
361        let bytes = [0x12, 0x34, 0x56, 0x78];
362        let mut r1 = WireReader::<true>::new(&bytes);
363        let str1 = r1.read_ref(4);
364
365        assert!(str1 == Ok("\x12\x34\x56\x78"));
366        assert!(r1.finalize() == Ok(()));
367    }
368
369    #[test]
370    fn read_nonutf8_str_fail() {
371        let bytes = [0x9a, 0xbc, 0xde, 0xf0];
372        let mut r1 = WireReader::<true>::new(&bytes);
373        let str1 = r1.read_ref::<str>(4);
374
375        assert!(str1.is_err());
376        assert!(match str1 {
377            Err(WireError::InvalidData(_)) => true,
378            _ => false,
379        });
380    }
381
382    struct CustomWireReadable<'a> {
383        a: u8,
384        b: &'a str,
385    }
386
387    impl<'a> WireReadComp<'a> for CustomWireReadable<'a> {
388        fn read_wire_comp<const E: bool>(curs: &mut WireCursor<'a>) -> Result<Self, WireError> {
389            // curs needs some stronger functionality
390
391            let a = curs.get_readable::<u8, E>()?;
392            let str_len = curs.get_readable::<u16, E>()?;
393
394            Ok(CustomWireReadable {
395                a: a,
396                b: curs.get_readable_ref::<str, E>(str_len as usize)?,
397            })
398        }
399    }
400
401    #[test]
402    fn custom_wire_readable() {
403        let bytes = [0x9a, 0x00, 0x05, 0x65, 0x66, 0x67, 0x68, 0x69];
404        let c1: CustomWireReadable;
405
406        // c1's lifetime must be bound to `bytes`, not `r1`, so this should be able to compile
407        {
408            let mut r1 = WireReader::<true>::new(&bytes);
409            c1 = r1.read_comp().unwrap_or(CustomWireReadable { a: 0, b: "" });
410
411            assert!(r1.is_empty())
412        }
413
414        assert!(c1.a == 0x9a);
415        assert!(c1.b == "\x65\x66\x67\x68\x69")
416    }
417}