1 //! `tor-bytes`: Utilities to decode/encode things into bytes. 2 //! 3 //! # Overview 4 //! 5 //! The `tor-bytes` crate is part of 6 //! [Arti](https://gitlab.torproject.org/tpo/core/arti/), a project to 7 //! implement [Tor](https://www.torproject.org/) in Rust. 8 //! Other crates in Arti use it to build and handle all the byte-encoded 9 //! objects from the Tor protocol. For textual directory items, see 10 //! the [`tor-netdoc`] crate. 11 //! 12 //! This crate is generally useful for encoding and decoding 13 //! byte-oriented formats that are not regular enough to qualify for 14 //! serde, and not complex enough to need a full meta-language. It is 15 //! probably not suitable for handling anything bigger than a few 16 //! kilobytes in size. 17 //! 18 //! ## Alternatives 19 //! 20 //! The Reader/Writer traits in std::io are more appropriate for 21 //! operations that can fail because of some IO problem. This crate 22 //! can't handle that: it is for handling things that are already in 23 //! memory. 24 //! 25 //! TODO: Look into using the "bytes" crate more here. 26 //! 27 //! TODO: The "untrusted" crate has similar goals to our [`Reader`], 28 //! but takes more steps to make sure it can never panic. Perhaps we 29 //! should see if we can learn any tricks from it. 30 //! 31 //! TODO: Do we really want to keep `Reader` as a struct and 32 //! `Writer` as a trait? 33 //! 34 //! # Contents and concepts 35 //! 36 //! This crate is structured around four key types: 37 //! 38 //! * [`Reader`]: A view of a byte slice, from which data can be decoded. 39 //! * [`Writer`]: Trait to represent a growable buffer of bytes. 40 //! (Vec<u8> and [`bytes::BytesMut`] implement this.) 41 //! * [`Writeable`]: Trait for an object that can be encoded onto a [`Writer`] 42 //! * [`Readable`]: Trait for an object that can be decoded from a [`Reader`]. 43 //! 44 //! Every object you want to encode or decode should implement 45 //! [`Writeable`] or [`Readable`] respectively. 46 //! 47 //! Once you implement these traits, you can use Reader and Writer to 48 //! handle your type, and other types that are built around it. 49 50 #![deny(missing_docs)] 51 #![warn(noop_method_call)] 52 #![deny(unreachable_pub)] 53 #![deny(clippy::await_holding_lock)] 54 #![deny(clippy::cargo_common_metadata)] 55 #![deny(clippy::cast_lossless)] 56 #![deny(clippy::checked_conversions)] 57 #![warn(clippy::clone_on_ref_ptr)] 58 #![warn(clippy::cognitive_complexity)] 59 #![deny(clippy::debug_assert_with_mut_call)] 60 #![deny(clippy::exhaustive_enums)] 61 #![deny(clippy::exhaustive_structs)] 62 #![deny(clippy::expl_impl_clone_on_copy)] 63 #![deny(clippy::fallible_impl_from)] 64 #![deny(clippy::implicit_clone)] 65 #![deny(clippy::large_stack_arrays)] 66 #![warn(clippy::manual_ok_or)] 67 #![deny(clippy::missing_docs_in_private_items)] 68 #![deny(clippy::missing_panics_doc)] 69 #![warn(clippy::needless_borrow)] 70 #![warn(clippy::needless_pass_by_value)] 71 #![warn(clippy::option_option)] 72 #![warn(clippy::rc_buffer)] 73 #![deny(clippy::ref_option_ref)] 74 #![warn(clippy::semicolon_if_nothing_returned)] 75 #![warn(clippy::trait_duplication_in_bounds)] 76 #![deny(clippy::unnecessary_wraps)] 77 #![warn(clippy::unseparated_literal_suffix)] 78 #![deny(clippy::unwrap_used)] 79 80 mod err; 81 mod impls; 82 mod reader; 83 mod writer; 84 85 pub use err::Error; 86 pub use reader::Reader; 87 pub use writer::Writer; 88 89 use arrayref::array_ref; 90 91 /// Result type returned by this crate. 92 pub type Result<T> = std::result::Result<T, Error>; 93 94 /// Trait for an object that can be encoded onto a Writer by reference. 95 /// 96 /// Implement this trait in order to make an object writeable. 97 /// 98 /// Most code won't need to call this directly, but will instead use 99 /// it implicitly via the Writer::write() method. 100 /// 101 /// # Example 102 /// 103 /// ``` 104 /// use tor_bytes::{Writeable, Writer}; 105 /// #[derive(Debug, Eq, PartialEq)] 106 /// struct Message { 107 /// flags: u32, 108 /// cmd: u8 109 /// } 110 /// 111 /// impl Writeable for Message { 112 /// fn write_onto<B:Writer+?Sized>(&self, b: &mut B) { 113 /// // We'll say that a "Message" is encoded as flags, then command. 114 /// b.write_u32(self.flags); 115 /// b.write_u8(self.cmd); 116 /// } 117 /// } 118 /// 119 /// let msg = Message { flags: 0x43, cmd: 0x07 }; 120 /// let mut writer: Vec<u8> = Vec::new(); 121 /// writer.write(&msg); 122 /// assert_eq!(writer, &[0x00, 0x00, 0x00, 0x43, 0x07 ]); 123 /// ``` 124 pub trait Writeable { 125 /// Encode this object into the writer `b`. write_onto<B: Writer + ?Sized>(&self, b: &mut B)126 fn write_onto<B: Writer + ?Sized>(&self, b: &mut B); 127 } 128 129 /// Trait for an object that can be encoded and consumed by a Writer. 130 /// 131 /// Implement this trait in order to make an object that can be 132 /// written more efficiently by absorbing it into the writer. 133 /// 134 /// Most code won't need to call this directly, but will instead use 135 /// it implicitly via the Writer::write_and_consume() method. 136 pub trait WriteableOnce { 137 /// Encode this object into the writer `b`, and consume it. write_into<B: Writer + ?Sized>(self, b: &mut B)138 fn write_into<B: Writer + ?Sized>(self, b: &mut B); 139 } 140 141 impl<W: Writeable> WriteableOnce for W { write_into<B: Writer + ?Sized>(self, b: &mut B)142 fn write_into<B: Writer + ?Sized>(self, b: &mut B) { 143 self.write_onto(b); 144 } 145 } 146 147 // ---------------------------------------------------------------------- 148 149 /// Trait for an object that can be extracted from a Reader. 150 /// 151 /// Implement this trait in order to make an object that can (maybe) 152 /// be decoded from a reader. 153 // 154 /// Most code won't need to call this directly, but will instead use 155 /// it implicitly via the Reader::extract() method. 156 /// 157 /// # Example 158 /// 159 /// ``` 160 /// use tor_bytes::{Readable,Reader,Result}; 161 /// #[derive(Debug, Eq, PartialEq)] 162 /// struct Message { 163 /// flags: u32, 164 /// cmd: u8 165 /// } 166 /// 167 /// impl Readable for Message { 168 /// fn take_from(r: &mut Reader<'_>) -> Result<Self> { 169 /// // A "Message" is encoded as flags, then command. 170 /// let flags = r.take_u32()?; 171 /// let cmd = r.take_u8()?; 172 /// Ok(Message{ flags, cmd }) 173 /// } 174 /// } 175 /// 176 /// let encoded = [0x00, 0x00, 0x00, 0x43, 0x07 ]; 177 /// let mut reader = Reader::from_slice(&encoded); 178 /// let m: Message = reader.extract()?; 179 /// assert_eq!(m, Message { flags: 0x43, cmd: 0x07 }); 180 /// reader.should_be_exhausted()?; // make sure there are no bytes left over 181 /// # Result::Ok(()) 182 /// ``` 183 pub trait Readable: Sized { 184 /// Try to extract an object of this type from a Reader. 185 /// 186 /// Implementations should generally try to be efficient: this is 187 /// not the right place to check signatures or perform expensive 188 /// operations. If you have an object that must not be used until 189 /// it is finally validated, consider making this function return 190 /// a wrapped type that can be unwrapped later on once it gets 191 /// checked. take_from(b: &mut Reader<'_>) -> Result<Self>192 fn take_from(b: &mut Reader<'_>) -> Result<Self>; 193 } 194 195 // ---------------------------------------------------------------------- 196 197 #[cfg(test)] 198 mod test { 199 use super::*; 200 201 #[test] writer()202 fn writer() { 203 let mut v: Vec<u8> = Vec::new(); 204 v.write_u8(0x57); 205 v.write_u16(0x6520); 206 v.write_u32(0x68617665); 207 v.write_u64(0x2061206d61636869); 208 v.write_all(b"ne in a plexiglass dome"); 209 v.write_zeros(3); 210 assert_eq!(&v[..], &b"We have a machine in a plexiglass dome\0\0\0"[..]); 211 } 212 } 213