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}