├── .gitignore ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── README.md ├── benches └── lib.rs ├── src ├── ctx │ ├── bool.rs │ ├── bytes.rs │ ├── mod.rs │ ├── num.rs │ └── str.rs └── lib.rs └── tests └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target/ 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | 3 | sudo: required 4 | 5 | matrix: 6 | include: 7 | - rust: stable 8 | - rust: beta 9 | - rust: nightly 10 | 11 | branches: 12 | only: 13 | - master 14 | 15 | script: 16 | - | 17 | cargo test --verbose 18 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "byte" 3 | version = "0.2.7" 4 | edition = "2021" 5 | authors = ["andylokandy"] 6 | license = "MIT/Apache-2.0" 7 | 8 | description = "A low-level, zero-copy and panic-free serializer and deserializer for binary." 9 | documentation = "https://docs.rs/byte" 10 | repository = "https://github.com/andylokandy/byte" 11 | homepage = "https://github.com/andylokandy/byte" 12 | readme = "README.md" 13 | 14 | keywords = ["binary", "parser", "bytes", "scroll", "no_std"] 15 | categories = ["no-std", "embedded", "encoding", "parsing"] 16 | 17 | [dev-dependencies] 18 | quickcheck = "0.3" 19 | byteorder = "1.0.0" 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Andy Lok 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `Byte` 2 | 3 | [![build status](https://travis-ci.org/andylokandy/byte.svg?branch=master)](https://travis-ci.org/andylokandy/byte) 4 | [![crates.io](https://img.shields.io/crates/v/byte.svg)](https://crates.io/crates/byte) 5 | [![docs.rs](https://docs.rs/byte/badge.svg)](https://docs.rs/byte) 6 | 7 | A low-level, zero-copy and panic-free binary serializer and deserializer. 8 | 9 | ### [**Documentation**](https://docs.rs/byte) 10 | 11 | ## Usage 12 | 13 | Add the following to your `Cargo.toml`: 14 | ```toml 15 | [dependencies] 16 | byte = "0.2" 17 | ``` 18 | 19 | `Byte` is a `no_std` library; it can be used in any `#![no_std]` situation or crate. 20 | 21 | 22 | # Overview 23 | 24 | `Byte` is designed to encode or decode binary data in a fast and low-level way. 25 | A classical use case is I2C communication en/decoding. 26 | 27 | `Byte` provides two core traits `TryRead` and `TryWrite`. 28 | Types that implement these traits can be serialized into or deserialized from byte slices. 29 | 30 | The library is meant to be simple, and it will always be. 31 | 32 | 33 | # Example 34 | 35 | ```rust 36 | use byte::*; 37 | 38 | let bytes: &[u8] = &[0xde, 0xad, 0xbe, 0xef]; 39 | 40 | let offset = &mut 0; 41 | let num = bytes.read_with::(offset, BE).unwrap(); 42 | assert_eq!(num, 0xdeadbeef); 43 | assert_eq!(*offset, 4); 44 | ``` 45 | 46 | ```rust 47 | use byte::*; 48 | use byte::ctx::{Str, NULL}; 49 | 50 | let bytes: &[u8] = b"hello, world!\0dump"; 51 | 52 | let offset = &mut 0; 53 | let str = bytes.read_with::<&str>(offset, Str::Delimiter(NULL)).unwrap(); 54 | assert_eq!(str, "hello, world!"); 55 | assert_eq!(*offset, 14); 56 | ``` 57 | 58 | 59 | ## Contribution 60 | 61 | All kinds of contribution are welcomed. 62 | 63 | - **Issues.** Feel free to open an issue when you find typos, bugs, or have any question. 64 | - **Pull requests.** New collection, better implementation, more tests, more documents and typo fixes are all welcomed. 65 | 66 | 67 | ## License 68 | 69 | Licensed under MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 70 | -------------------------------------------------------------------------------- /benches/lib.rs: -------------------------------------------------------------------------------- 1 | #![feature(test)] 2 | 3 | extern crate byte; 4 | extern crate byteorder; 5 | extern crate test; 6 | 7 | use byte::ctx::*; 8 | use byte::*; 9 | use byteorder::*; 10 | use test::black_box; 11 | 12 | #[bench] 13 | fn bench_byteorder(b: &mut test::Bencher) { 14 | b.iter(|| black_box(LittleEndian::read_u16(&black_box([1, 2])))); 15 | b.bytes = 2; 16 | } 17 | 18 | #[bench] 19 | fn bench_read_num(b: &mut test::Bencher) { 20 | b.iter(|| black_box(black_box([1, 2]).read_with::(&mut 0, LE).unwrap())); 21 | b.bytes = 2; 22 | } 23 | 24 | #[bench] 25 | fn bench_str(b: &mut test::Bencher) { 26 | let bytes = b"abcdefghijkl"; 27 | b.iter(|| { 28 | black_box( 29 | black_box(bytes) 30 | .read_with::<&str>(&mut 0, Str::Len(5)) 31 | .unwrap(), 32 | ) 33 | }); 34 | b.bytes = 5; 35 | } 36 | 37 | #[bench] 38 | fn bench_str_hardcode(b: &mut test::Bencher) { 39 | let bytes = b"abcdefghijkl"; 40 | b.iter(|| black_box(std::str::from_utf8(&black_box(bytes)[0..5]).unwrap())); 41 | b.bytes = 5; 42 | } 43 | 44 | #[bench] 45 | fn bench_example_read(b: &mut test::Bencher) { 46 | let bytes = black_box([0, 5, b"H"[0], b"E"[0], b"L"[0], b"L"[0], b"O"[0], 0]); 47 | b.iter(|| black_box(bytes.read_with::
(&mut 0, BE).unwrap())); 48 | b.bytes = 8; 49 | } 50 | 51 | #[bench] 52 | fn bench_example_write(b: &mut test::Bencher) { 53 | let mut bytes = [0u8; 8]; 54 | b.iter(|| { 55 | let header = black_box(Header { 56 | name: "HELLO", 57 | enabled: false, 58 | }); 59 | bytes.write_with::
(&mut 0, header, BE).unwrap() 60 | }); 61 | b.bytes = 8; 62 | } 63 | 64 | #[bench] 65 | fn bench_example_read_hardcode(b: &mut test::Bencher) { 66 | let bytes = black_box([0, 5, b"H"[0], b"E"[0], b"L"[0], b"L"[0], b"O"[0], 0]); 67 | b.iter(|| black_box(example_read_hardcode(&bytes[..]).unwrap())); 68 | b.bytes = 8; 69 | } 70 | 71 | #[bench] 72 | fn bench_example_write_hardcode(b: &mut test::Bencher) { 73 | let mut bytes = [0u8; 8]; 74 | b.iter(|| { 75 | let header = black_box(Header { 76 | name: "HELLO", 77 | enabled: false, 78 | }); 79 | example_write_hardcode(&mut bytes[..], header).unwrap() 80 | }); 81 | b.bytes = 8; 82 | } 83 | 84 | fn example_read_hardcode<'a>(bytes: &'a [u8]) -> Option> { 85 | if bytes.len() < 3 { 86 | return None; 87 | } 88 | 89 | let name_len = unsafe { *(&bytes[0] as *const _ as *const u16) }; 90 | let name_len = name_len.to_be() as usize; 91 | 92 | if bytes.len() < name_len + 3 { 93 | return None; 94 | } 95 | 96 | let name = std::str::from_utf8(&bytes[2..name_len + 2]).unwrap(); 97 | let enabled = bytes[name_len + 2] != 0; 98 | 99 | Some(Header { name, enabled }) 100 | } 101 | 102 | fn example_write_hardcode(bytes: &mut [u8], header: Header) -> Option<()> { 103 | if bytes.len() < header.name.len() + 3 { 104 | return None; 105 | } 106 | 107 | unsafe { *(&mut bytes[0] as *mut _ as *mut u16) = header.name.len().to_be() as u16 }; 108 | 109 | bytes[2..header.name.len() + 2].clone_from_slice(header.name.as_bytes()); 110 | bytes[header.name.len()] = if header.enabled { u8::max_value() } else { 0 }; 111 | 112 | Some(()) 113 | } 114 | 115 | struct Header<'a> { 116 | name: &'a str, 117 | enabled: bool, 118 | } 119 | 120 | impl<'a> TryRead<'a, Endian> for Header<'a> { 121 | fn try_read(bytes: &'a [u8], endian: Endian) -> Result<(Self, usize)> { 122 | let offset = &mut 0; 123 | 124 | let name_len = black_box(bytes.read_with::(offset, endian)? as usize); 125 | let name = bytes.read_with::<&str>(offset, Str::Len(name_len))?; 126 | let enabled = bytes.read(offset)?; 127 | 128 | Ok((Header { name, enabled }, *offset)) 129 | } 130 | } 131 | 132 | impl<'a> TryWrite for Header<'a> { 133 | fn try_write(self, bytes: &mut [u8], endian: Endian) -> Result { 134 | let offset = &mut 0; 135 | 136 | bytes.write_with(offset, self.name.len() as u16, endian)?; 137 | bytes.write(offset, self.name)?; 138 | bytes.write(offset, self.enabled)?; 139 | 140 | Ok(*offset) 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/ctx/bool.rs: -------------------------------------------------------------------------------- 1 | use crate::{check_len, Result, TryRead, TryWrite}; 2 | 3 | impl<'a> TryRead<'a> for bool { 4 | #[inline] 5 | fn try_read(bytes: &'a [u8], _ctx: ()) -> Result<(Self, usize)> { 6 | check_len(bytes, 1)?; 7 | 8 | Ok((bytes[0] != 0, 1)) 9 | } 10 | } 11 | 12 | impl TryWrite for bool { 13 | #[inline] 14 | fn try_write(self, bytes: &mut [u8], _ctx: ()) -> Result { 15 | check_len(bytes, 1)?; 16 | 17 | bytes[0] = if self { u8::max_value() } else { 0 }; 18 | 19 | Ok(1) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/ctx/bytes.rs: -------------------------------------------------------------------------------- 1 | use crate::{check_len, Error, Result, TryRead, TryWrite}; 2 | 3 | /// Context for &[u8] to determine where the slice ends. 4 | /// 5 | /// Pattern will be included in the result 6 | /// 7 | /// # Example 8 | /// 9 | /// ``` 10 | /// use byte::*; 11 | /// use byte::ctx::*; 12 | /// 13 | /// let bytes: &[u8] = &[0xde, 0xad, 0xbe, 0xef, 0x00, 0xff]; 14 | /// 15 | /// let sub: &[u8] = bytes.read_with(&mut 0, Bytes::Len(2)).unwrap(); 16 | /// assert_eq!(sub, &[0xde, 0xad]); 17 | /// 18 | /// static PATTERN: &'static [u8; 2] = &[0x00, 0xff]; 19 | /// 20 | /// let sub: &[u8] = bytes.read_with(&mut 0, Bytes::Pattern(PATTERN)).unwrap(); 21 | /// assert_eq!(sub, &[0xde, 0xad, 0xbe, 0xef, 0x00, 0xff]); 22 | /// 23 | /// let sub: &[u8] = bytes.read_with(&mut 0, Bytes::PatternUntil(PATTERN, 4)).unwrap(); 24 | /// assert_eq!(sub, &[0xde, 0xad, 0xbe, 0xef]); 25 | /// ``` 26 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 27 | pub enum Bytes { 28 | /// Take fix-length bytes 29 | Len(usize), 30 | /// Take bytes until reaching a byte pattern 31 | Pattern(&'static [u8]), 32 | /// Take bytes until either byte pattern or length reached 33 | PatternUntil(&'static [u8], usize), 34 | } 35 | 36 | impl<'a> TryRead<'a, Bytes> for &'a [u8] { 37 | #[inline] 38 | fn try_read(bytes: &'a [u8], ctx: Bytes) -> Result<(Self, usize)> { 39 | let len = match ctx { 40 | Bytes::Len(len) => check_len(bytes, len)?, 41 | Bytes::Pattern(pattern) => { 42 | if pattern.is_empty() { 43 | return Err(Error::BadInput { 44 | err: "Pattern is empty", 45 | }); 46 | } 47 | check_len(bytes, pattern.len())?; 48 | (0..bytes.len() - pattern.len() + 1) 49 | .map(|n| bytes[n..].starts_with(pattern)) 50 | .position(|p| p) 51 | .map(|len| len + pattern.len()) 52 | .ok_or(Error::Incomplete)? 53 | } 54 | Bytes::PatternUntil(pattern, len) => { 55 | if pattern.is_empty() { 56 | return Err(Error::BadInput { 57 | err: "Pattern is empty", 58 | }); 59 | } 60 | if pattern.len() > len { 61 | return Err(Error::BadInput { 62 | err: "Pattern is longer than restricted length", 63 | }); 64 | } 65 | check_len(bytes, pattern.len())?; 66 | (0..bytes.len() - pattern.len() + 1) 67 | .map(|n| bytes[n..].starts_with(pattern)) 68 | .take(len - pattern.len()) 69 | .position(|p| p) 70 | .map(|position| position + pattern.len()) 71 | .unwrap_or(check_len(bytes, len)?) 72 | } 73 | }; 74 | 75 | Ok((&bytes[..len], len)) 76 | } 77 | } 78 | 79 | impl<'a> TryWrite for &'a [u8] { 80 | #[inline] 81 | fn try_write(self, bytes: &mut [u8], _ctx: ()) -> Result { 82 | check_len(bytes, self.len())?; 83 | 84 | bytes[..self.len()].clone_from_slice(self); 85 | 86 | Ok(self.len()) 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/ctx/mod.rs: -------------------------------------------------------------------------------- 1 | //! Context for primitives 2 | 3 | mod bool; 4 | mod bytes; 5 | mod num; 6 | mod str; 7 | 8 | pub use self::bytes::*; 9 | pub use self::num::*; 10 | pub use self::str::*; 11 | -------------------------------------------------------------------------------- /src/ctx/num.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_parens)] 2 | 3 | use crate::{check_len, Error, Result, TryRead, TryWrite}; 4 | use core::convert::TryInto; 5 | use core::mem; 6 | 7 | /// Endiannes of numbers. 8 | /// 9 | /// Defaults to the machine's endianness. 10 | /// 11 | /// # Example 12 | /// 13 | /// ``` 14 | /// use byte::*; 15 | /// 16 | /// let bytes: &[u8] = &[0x00, 0xff]; 17 | /// 18 | /// let num_be: u16 = bytes.read_with(&mut 0, BE).unwrap(); 19 | /// assert_eq!(num_be, 0x00ff); 20 | /// 21 | /// let num_le: u16 = bytes.read_with(&mut 0, LE).unwrap(); 22 | /// assert_eq!(num_le, 0xff00); 23 | /// ``` 24 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 25 | pub enum Endian { 26 | /// Little Endian byte order context 27 | Little, 28 | /// Big Endian byte order context 29 | Big, 30 | } 31 | 32 | impl Default for Endian { 33 | #[inline] 34 | fn default() -> Self { 35 | NATIVE 36 | } 37 | } 38 | 39 | /// Little endian byte order 40 | pub const LE: Endian = Endian::Little; 41 | /// Big endian byte order 42 | pub const BE: Endian = Endian::Big; 43 | 44 | /// Network endian 45 | pub const NETWORK: Endian = Endian::Little; 46 | 47 | /// The machine's native endianness 48 | #[cfg(target_endian = "little")] 49 | pub const NATIVE: Endian = LE; 50 | /// The machine's native endiannes 51 | #[cfg(target_endian = "big")] 52 | pub const NATIVE: Endian = BE; 53 | 54 | macro_rules! num_impl { 55 | ($ty: ty, $size: tt) => { 56 | impl<'a> TryRead<'a, Endian> for $ty { 57 | #[inline] 58 | fn try_read(bytes: &'a [u8], endian: Endian) -> Result<(Self, usize)> { 59 | check_len(bytes, $size)?; 60 | 61 | let val = match endian { 62 | Endian::Big => { 63 | <$ty>::from_be_bytes(bytes[..$size].try_into().map_err(|_e| { 64 | Error::BadInput { 65 | err: "TryIntoSliceError", 66 | } 67 | })?) 68 | } 69 | Endian::Little => { 70 | <$ty>::from_le_bytes(bytes[..$size].try_into().map_err(|_e| { 71 | Error::BadInput { 72 | err: "TryIntoSliceError", 73 | } 74 | })?) 75 | } 76 | }; 77 | 78 | Ok((val, $size)) 79 | } 80 | } 81 | 82 | impl TryWrite for $ty { 83 | #[inline] 84 | fn try_write(self, bytes: &mut [u8], endian: Endian) -> Result { 85 | check_len(bytes, $size)?; 86 | 87 | let _val = match endian { 88 | Endian::Big => bytes[..$size].copy_from_slice(&self.to_be_bytes()), 89 | Endian::Little => bytes[..$size].copy_from_slice(&self.to_le_bytes()), 90 | }; 91 | 92 | Ok($size) 93 | } 94 | } 95 | }; 96 | } 97 | 98 | num_impl!(u8, 1); 99 | num_impl!(u16, 2); 100 | num_impl!(u32, 4); 101 | num_impl!(u64, 8); 102 | num_impl!(i8, 1); 103 | num_impl!(i16, 2); 104 | num_impl!(i32, 4); 105 | num_impl!(i64, 8); 106 | num_impl!(usize, (mem::size_of::())); 107 | num_impl!(isize, (mem::size_of::())); 108 | 109 | macro_rules! float_impl { 110 | ($ty: ty, $base: ty) => { 111 | impl<'a> TryRead<'a, Endian> for $ty { 112 | #[inline] 113 | fn try_read(bytes: &'a [u8], endian: Endian) -> Result<(Self, usize)> { 114 | <$base as TryRead<'a, Endian>>::try_read(bytes, endian) 115 | .map(|(val, size)| (<$ty>::from_bits(val), size)) 116 | } 117 | } 118 | 119 | impl<'a> TryWrite for $ty { 120 | #[inline] 121 | fn try_write(self, bytes: &mut [u8], endian: Endian) -> Result { 122 | <$base as TryWrite>::try_write(self.to_bits(), bytes, endian) 123 | } 124 | } 125 | }; 126 | } 127 | 128 | float_impl!(f32, u32); 129 | float_impl!(f64, u64); 130 | -------------------------------------------------------------------------------- /src/ctx/str.rs: -------------------------------------------------------------------------------- 1 | use crate::{check_len, Error, Result, TryRead, TryWrite}; 2 | use core::str; 3 | 4 | /// Context for &str to determine where a &str ends. 5 | /// 6 | /// Pattern will **not** be included in the result 7 | /// 8 | /// Default to `NULL` delimiter. 9 | /// 10 | /// # Example 11 | /// 12 | /// ``` 13 | /// use byte::*; 14 | /// use byte::ctx::*; 15 | /// 16 | /// let bytes: &[u8] = b"hello, world!\0"; 17 | /// 18 | /// let str: &str = bytes.read(&mut 0).unwrap(); 19 | /// assert_eq!(str, "hello, world!"); 20 | /// 21 | /// let str: &str = bytes.read_with(&mut 0, Str::Len(5)).unwrap(); 22 | /// assert_eq!(str, "hello"); 23 | /// 24 | /// let str: &str = bytes.read_with(&mut 0, Str::Delimiter(b"!"[0])).unwrap(); 25 | /// assert_eq!(str, "hello, world"); 26 | /// 27 | /// let str: &str = bytes.read_with(&mut 0, Str::DelimiterUntil(NULL, 5)).unwrap(); 28 | /// assert_eq!(str, "hello"); 29 | /// ``` 30 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 31 | pub enum Str { 32 | /// Take fix-length bytes as str 33 | Len(usize), 34 | /// Take bytes until reaching a delimiter 35 | Delimiter(u8), 36 | /// Take bytes until either delimiter or length reached 37 | DelimiterUntil(u8, usize), 38 | } 39 | 40 | impl Default for Str { 41 | #[inline] 42 | fn default() -> Self { 43 | Str::Delimiter(NULL) 44 | } 45 | } 46 | 47 | /// Null string delimiter 48 | pub const NULL: u8 = 0; 49 | /// Space string delimiter 50 | pub const SPACE: u8 = 0x20; 51 | /// Return string delimiter 52 | pub const RET: u8 = 0x0a; 53 | /// Tab string delimiter 54 | pub const TAB: u8 = 0x09; 55 | 56 | impl<'a> TryRead<'a, Str> for &'a str { 57 | #[inline] 58 | fn try_read(bytes: &'a [u8], ctx: Str) -> Result<(Self, usize)> { 59 | let (bytes, size) = match ctx { 60 | Str::Len(len) => { 61 | let len = check_len(bytes, len)?; 62 | (&bytes[..len], len) 63 | } 64 | Str::Delimiter(delimiter) => { 65 | let position = bytes 66 | .iter() 67 | .position(|c| *c == delimiter) 68 | .ok_or(Error::Incomplete)?; 69 | (&bytes[..position], position + 1) 70 | } 71 | Str::DelimiterUntil(delimiter, len) => { 72 | let position = bytes.iter().take(len).position(|c| *c == delimiter); 73 | match position { 74 | Some(position) => (&bytes[..position], position + 1), 75 | None => { 76 | let len = check_len(bytes, len)?; 77 | (&bytes[..len], len) 78 | } 79 | } 80 | } 81 | }; 82 | 83 | match str::from_utf8(bytes) { 84 | Ok(str) => Ok((str, size)), 85 | Err(_) => Err(Error::BadInput { err: "UTF8 Error" }), 86 | } 87 | } 88 | } 89 | 90 | impl<'a> TryWrite for &'a str { 91 | #[inline] 92 | fn try_write(self, bytes: &mut [u8], _ctx: ()) -> Result { 93 | let str_bytes = self.as_bytes(); 94 | 95 | check_len(bytes, str_bytes.len())?; 96 | 97 | bytes[..str_bytes.len()].clone_from_slice(str_bytes); 98 | 99 | Ok(str_bytes.len()) 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A low-level, zero-copy and panic-free binary serializer and deserializer. 2 | //! 3 | //! # Usage 4 | //! 5 | //! First, add the following to your `Cargo.toml`: 6 | //! 7 | //! ```toml 8 | //! [dependencies] 9 | //! byte = "0.3" 10 | //! ``` 11 | //! 12 | //! `Byte` is a `no_std` library; it can be used in any `#![no_std]` situation or crate. 13 | //! 14 | //! # Overview 15 | //! 16 | //! `Byte` is designed for encoding or decoding binary data in a fast and low level way. 17 | //! A classical use case is I2C communication packages encoding. 18 | //! 19 | //! `Byte` provides two core traits `TryRead` and `TryWrite`. 20 | //! Types implement these traits can be serialized into or deserialized from byte slices. 21 | //! 22 | //! The library is meant to be simple, and it will always be. 23 | //! 24 | //! # Examples 25 | //! 26 | //! Deserialize a `u32` from bytes: 27 | //! 28 | //! ``` 29 | //! use byte::*; 30 | //! 31 | //! let bytes: &[u8] = &[0xde, 0xad, 0xbe, 0xef]; 32 | //! 33 | //! let offset = &mut 0; 34 | //! let num = bytes.read_with::(offset, BE).unwrap(); 35 | //! assert_eq!(num, 0xdeadbeef); 36 | //! assert_eq!(*offset, 4); 37 | //! ``` 38 | //! 39 | //! Deserialize a `&str` from bytes: 40 | //! 41 | //! ``` 42 | //! use byte::*; 43 | //! use byte::ctx::{Str, NULL}; 44 | //! 45 | //! let bytes: &[u8] = b"hello, world!\0dump"; 46 | //! 47 | //! let offset = &mut 0; 48 | //! let str = bytes.read_with::<&str>(offset, Str::Delimiter(NULL)).unwrap(); 49 | //! assert_eq!(str, "hello, world!"); 50 | //! assert_eq!(*offset, 14); 51 | //! ``` 52 | //! 53 | //! `Byte` supports serializing and deserializing language primitives by default. 54 | //! 55 | //! - `&str` (with `Str` context) 56 | //! - `&[u8]` (with `Byte` context) 57 | //! - `u8`, `i8`, `u64`, `f64` ... (with `Endian` context) 58 | //! - `bool` 59 | //! 60 | //! # Define custom serializable/deserializable types 61 | //! 62 | //! In this example, we implement `TryRead` and `TryWrite` for the `Header` type, 63 | //! which has a variable-length name and a boolean field. 64 | //! 65 | //! ## Binary Structure 66 | //! 67 | //! ```text 68 | //! | | Name's Length (Big Endian) | Name | Enabled | 69 | //! | ----- | -------------------------- | ---- | ---- | ---- | ---- | ---- | ------- | 70 | //! | Byte | 0 | 5 | 'H' | 'E' | 'L' | 'L' | 'O' | 0 | 71 | //! ``` 72 | //! 73 | //! ## Example 74 | //! 75 | //! The only thing you may be curious about is the returned usize; 76 | //! that's the number of bytes consumed by the read/write operation. 77 | //! 78 | //! ``` 79 | //! use byte::*; 80 | //! use byte::ctx::*; 81 | //! 82 | //! struct Header<'a> { 83 | //! name: &'a str, 84 | //! enabled: bool, 85 | //! } 86 | //! 87 | //! impl<'a> TryRead<'a, Endian> for Header<'a> { 88 | //! fn try_read(bytes: &'a [u8], endian: Endian) -> Result<(Self, usize)> { 89 | //! let offset = &mut 0; 90 | //! 91 | //! let name_len = bytes.read_with::(offset, endian)? as usize; 92 | //! let header = Header { 93 | //! name: bytes.read_with::<&str>(offset, Str::Len(name_len))?, 94 | //! enabled: bytes.read::(offset)?, 95 | //! }; 96 | //! 97 | //! Ok((header, *offset)) 98 | //! } 99 | //! } 100 | //! 101 | //! impl<'a> TryWrite for Header<'a> { 102 | //! fn try_write(self, bytes: &mut [u8], endian: Endian) -> Result { 103 | //! let offset = &mut 0; 104 | //! 105 | //! bytes.write_with::(offset, self.name.len() as u16, endian)?; 106 | //! bytes.write::<&str>(offset, self.name)?; 107 | //! bytes.write::(offset, self.enabled)?; 108 | //! 109 | //! Ok(*offset) 110 | //! } 111 | //! } 112 | //! ``` 113 | //! 114 | //! ## Usage 115 | //! 116 | //! ```ignore 117 | //! let bytes = [0, 5, b"H"[0], b"E"[0], b"L"[0], b"L"[0], b"O"[0], 0]; 118 | //! 119 | //! let header: Header = bytes.read_with(&mut 0, BE).unwrap(); 120 | //! 121 | //! assert_eq!(header.name, "HELLO"); 122 | //! assert_eq!(header.enabled, false); 123 | //! 124 | //! let mut write = [0u8; 8]; 125 | //! write.write_with(&mut 0, header, BE).unwrap(); 126 | //! assert_eq!(write, bytes); 127 | //! ``` 128 | 129 | #![no_std] 130 | #![forbid(unsafe_code)] 131 | 132 | pub mod ctx; 133 | use core::marker::PhantomData; 134 | pub use ctx::{BE, LE}; 135 | 136 | /// A specialized Result type for `Byte` 137 | pub type Result = core::result::Result; 138 | 139 | /// The error type for the `byte` crate. 140 | /// 141 | /// - `Error::BadOffset` will be returned when the offset parameter exceeds the slice's length. 142 | /// 143 | /// - `Error::BadInput` and `Error::Incomplete` will be returned when `try_read()` or 144 | /// `try_write()` finds the bytes are invalid or not long enough to determine their validity. 145 | /// 146 | /// Note that we usually use `bytes.read()` in `try_read()` which may return `Error::BadOffset`, 147 | /// indicating incomplete data. So the error will automatically be converted into 148 | /// `Error::Incomplete` if you use `bytes.read()` (the same applies to `write()`). 149 | #[derive(Debug, PartialEq, Eq, Copy, Clone)] 150 | pub enum Error { 151 | /// The requested data is bigger than the available range 152 | Incomplete, 153 | /// The offset is invalid 154 | BadOffset(usize), 155 | /// The requested data content is invalid 156 | BadInput { err: &'static str }, 157 | } 158 | 159 | /// A helper function that checks whether the given length exceeded the length 160 | /// of the slice; returns `Err(Error::Incomplete)` otherwise. 161 | /// 162 | /// # Example 163 | /// 164 | /// ``` 165 | /// use byte::*; 166 | /// 167 | /// let bytes = [0u8; 4]; 168 | /// assert_eq!(check_len(&bytes, 4), Ok(4)); 169 | /// assert_eq!(check_len(&bytes, 5), Err(Error::Incomplete)); 170 | /// ``` 171 | #[inline] 172 | pub fn check_len(bytes: &[u8], len: usize) -> Result { 173 | if bytes.len() < len { 174 | Err(Error::Incomplete) 175 | } else { 176 | Ok(len) 177 | } 178 | } 179 | 180 | /// A data structure that can be deserialized. 181 | /// Types implementing this trait can be `read()` from a byte slice. 182 | pub trait TryRead<'a, Ctx = ()> 183 | where 184 | Self: Sized, 185 | { 186 | /// Try to read from a byte slice using a specific context. 187 | /// 188 | /// Read the value out of bytes; the bytes passed in are splitted by offset 189 | /// and should be read at head. 190 | /// If successful, `try_read()` should return a tuple with the value and the 191 | /// number of bytes consumed. 192 | /// 193 | /// # Example 194 | /// 195 | /// ``` 196 | /// use byte::*; 197 | /// 198 | /// // Demo type showing how to read boolean from bytes. 199 | /// // This functionality is already provided by this crate. 200 | /// pub struct Bool(bool); 201 | /// 202 | /// impl<'a> TryRead<'a> for Bool { 203 | /// #[inline] 204 | /// fn try_read(bytes: &'a [u8], _ctx: ()) -> Result<(Self, usize)> { 205 | /// check_len(bytes, 1)?; 206 | /// 207 | /// Ok((Bool(bytes[0] != 0), 1)) 208 | /// } 209 | /// } 210 | /// ``` 211 | fn try_read(bytes: &'a [u8], ctx: Ctx) -> Result<(Self, usize)>; 212 | } 213 | 214 | /// A data structure that can be serialized. 215 | /// Types implement this trait can be `write()` into a byte slice. 216 | pub trait TryWrite { 217 | /// Try to write to a byte slice using a specific context. 218 | /// 219 | /// Write the value into bytes; the bytes passed in are splitted by offset 220 | /// and should be written at head. 221 | /// If successful `try_write()` should return the number of bytes written. 222 | /// 223 | /// # Example 224 | /// 225 | /// ``` 226 | /// use byte::*; 227 | /// 228 | /// pub struct HasBool(bool); 229 | /// 230 | /// impl TryWrite for HasBool { 231 | /// #[inline] 232 | /// fn try_write(self, bytes: &mut [u8], _ctx: ()) -> Result { 233 | /// check_len(bytes, 1)?; 234 | /// 235 | /// bytes[0] = if self.0 { u8::max_value() } else { 0 }; 236 | /// 237 | /// Ok(1) 238 | /// } 239 | /// } 240 | /// ``` 241 | fn try_write(self, bytes: &mut [u8], ctx: Ctx) -> Result; 242 | } 243 | 244 | /// Extension methods for byte slices. 245 | /// 246 | /// # Offset 247 | /// 248 | /// The offset is the first parameter of each method. 249 | /// 250 | /// It tells the starting position, and will be increased by the number 251 | /// which will be increased by size the operation consumed. 252 | pub trait BytesExt { 253 | /// Reads a value from a byte slice using the default context. 254 | /// 255 | /// # Example 256 | /// 257 | /// ``` 258 | /// use byte::*; 259 | /// 260 | /// let bytes: &[u8] = &[0, 1]; 261 | /// 262 | /// let bool1: bool = bytes.read(&mut 0).unwrap(); 263 | /// let bool2: bool = bytes.read(&mut 1).unwrap(); 264 | /// 265 | /// assert_eq!(bool1, false); 266 | /// assert_eq!(bool2, true); 267 | /// ``` 268 | fn read<'a, T>(&'a self, offset: &mut usize) -> Result 269 | where 270 | T: TryRead<'a, Ctx>, 271 | Ctx: Default, 272 | { 273 | self.read_with(offset, Default::default()) 274 | } 275 | 276 | /// Reads a value from a byte slice specifying the context. 277 | /// 278 | /// # Example 279 | /// 280 | /// ``` 281 | /// use byte::*; 282 | /// use byte::ctx::*; 283 | /// 284 | /// let bytes: &[u8] = b"hello, world!"; 285 | /// 286 | /// let str: &str = bytes.read_with(&mut 0, Str::Delimiter(b"!"[0])).unwrap(); 287 | /// assert_eq!(str, "hello, world"); 288 | /// ``` 289 | fn read_with<'a, T>(&'a self, offset: &mut usize, ctx: Ctx) -> Result 290 | where 291 | T: TryRead<'a, Ctx>; 292 | 293 | /// Reads multiple values of the same type using an iterator. 294 | /// 295 | /// # Example 296 | /// 297 | /// ``` 298 | /// use byte::*; 299 | /// use byte::ctx::*; 300 | /// 301 | /// let bytes: &[u8] = b"hello\0world\0dead\0beef\0more"; 302 | /// let mut offset = 0; 303 | /// { 304 | /// let mut iter = bytes.read_iter(&mut offset, Str::Delimiter(NULL)); 305 | /// assert_eq!(iter.next(), Some("hello")); 306 | /// assert_eq!(iter.next(), Some("world")); 307 | /// assert_eq!(iter.next(), Some("dead")); 308 | /// assert_eq!(iter.next(), Some("beef")); 309 | /// assert_eq!(iter.next(), None); 310 | /// } 311 | /// assert_eq!(offset, 22); 312 | /// ``` 313 | fn read_iter<'a, 'i, T>(&'a self, offset: &'i mut usize, ctx: Ctx) -> Iter<'a, 'i, T, Ctx> 314 | where 315 | T: TryRead<'a, Ctx>, 316 | Ctx: Clone; 317 | 318 | /// Writes a value into a byte slice using the default context. 319 | /// 320 | /// # Example 321 | /// 322 | /// ``` 323 | /// use byte::*; 324 | /// 325 | /// let mut bytes = [0u8; 2]; 326 | /// 327 | /// bytes.write(&mut 0, false).unwrap(); 328 | /// bytes.write(&mut 1, true).unwrap(); 329 | /// 330 | /// assert_eq!(bytes, [0, 0xff]); 331 | /// ``` 332 | fn write(&mut self, offset: &mut usize, t: T) -> Result<()> 333 | where 334 | T: TryWrite, 335 | Ctx: Default, 336 | { 337 | self.write_with(offset, t, Default::default()) 338 | } 339 | 340 | /// Writes a value into a byte slice specifiying the context. 341 | /// 342 | /// # Example 343 | /// 344 | /// ``` 345 | /// use byte::*; 346 | /// use byte::ctx::*; 347 | /// 348 | /// let mut bytes_be = [0u8; 2]; 349 | /// let mut bytes_le = [0u8; 2]; 350 | /// 351 | /// bytes_be.write_with::(&mut 0, 0xff, BE).unwrap(); 352 | /// bytes_le.write_with::(&mut 0, 0xff, LE).unwrap(); 353 | /// 354 | /// assert_eq!(bytes_be, [0, 0xff]); 355 | /// assert_eq!(bytes_le, [0xff, 0]); 356 | /// ``` 357 | fn write_with(&mut self, offset: &mut usize, t: T, ctx: Ctx) -> Result<()> 358 | where 359 | T: TryWrite; 360 | } 361 | 362 | impl BytesExt for [u8] { 363 | #[inline] 364 | fn read_with<'a, T>(&'a self, offset: &mut usize, ctx: Ctx) -> Result 365 | where 366 | T: TryRead<'a, Ctx>, 367 | { 368 | let slice = self; 369 | 370 | if *offset > slice.len() { 371 | return Err(Error::BadOffset(*offset)); 372 | }; 373 | 374 | match TryRead::try_read(&slice[*offset..], ctx) { 375 | Ok((t, size)) => { 376 | *offset += size; 377 | Ok(t) 378 | } 379 | Err(Error::BadOffset(_)) => Err(Error::Incomplete), 380 | Err(err) => Err(err), 381 | } 382 | } 383 | 384 | fn read_iter<'a, 'i, T>(&'a self, offset: &'i mut usize, ctx: Ctx) -> Iter<'a, 'i, T, Ctx> 385 | where 386 | T: TryRead<'a, Ctx>, 387 | Ctx: Clone, 388 | { 389 | Iter { 390 | bytes: self, 391 | offset, 392 | ctx, 393 | phantom: PhantomData, 394 | } 395 | } 396 | 397 | fn write_with(&mut self, offset: &mut usize, t: T, ctx: Ctx) -> Result<()> 398 | where 399 | T: TryWrite, 400 | { 401 | let slice = self; 402 | 403 | if *offset > slice.len() { 404 | return Err(Error::BadOffset(*offset)); 405 | }; 406 | 407 | match TryWrite::try_write(t, &mut slice[*offset..], ctx) { 408 | Ok(size) => { 409 | *offset += size; 410 | Ok(()) 411 | } 412 | Err(Error::BadOffset(_)) => Err(Error::Incomplete), 413 | Err(err) => Err(err), 414 | } 415 | } 416 | } 417 | 418 | /// An iterator that reads values of the same type from a byte slice. 419 | /// 420 | /// # Example 421 | /// 422 | /// ``` 423 | /// use byte::*; 424 | /// use byte::ctx::*; 425 | /// 426 | /// let bytes: &[u8] = b"hello\0world\0dead\0beef\0more"; 427 | /// let mut offset = 0; 428 | /// { 429 | /// let mut iter = bytes.read_iter(&mut offset, Str::Delimiter(NULL)); 430 | /// assert_eq!(iter.next(), Some("hello")); 431 | /// assert_eq!(iter.next(), Some("world")); 432 | /// assert_eq!(iter.next(), Some("dead")); 433 | /// assert_eq!(iter.next(), Some("beef")); 434 | /// assert_eq!(iter.next(), None); 435 | /// } 436 | /// assert_eq!(offset, 22); 437 | /// ``` 438 | #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] 439 | pub struct Iter<'a, 'i, T, Ctx> 440 | where 441 | T: TryRead<'a, Ctx>, 442 | Ctx: Clone, 443 | { 444 | bytes: &'a [u8], 445 | offset: &'i mut usize, 446 | ctx: Ctx, 447 | phantom: PhantomData, 448 | } 449 | 450 | impl<'a, 'i, T, Ctx> Iterator for Iter<'a, 'i, T, Ctx> 451 | where 452 | T: TryRead<'a, Ctx>, 453 | Ctx: Clone, 454 | { 455 | type Item = T; 456 | 457 | #[inline] 458 | fn next(&mut self) -> Option { 459 | TryRead::try_read(&self.bytes[*self.offset..], self.ctx.clone()) 460 | .ok() 461 | .map(|(t, size)| { 462 | *self.offset += size; 463 | t 464 | }) 465 | } 466 | 467 | #[inline] 468 | fn size_hint(&self) -> (usize, Option) { 469 | (0, None) 470 | } 471 | } 472 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate quickcheck; 3 | extern crate byte; 4 | extern crate byteorder; 5 | 6 | use byte::ctx::*; 7 | use byte::*; 8 | use byteorder::*; 9 | 10 | #[test] 11 | fn test_str() { 12 | let bytes: &[u8] = b"abcd\0efg"; 13 | 14 | let mut offset = 0; 15 | assert_eq!( 16 | bytes 17 | .read_with::<&str>(&mut offset, Str::Delimiter(NULL)) 18 | .unwrap(), 19 | "abcd" 20 | ); 21 | assert_eq!(offset, 5); 22 | 23 | let bytes: &[u8] = b"abcdefghijklmnopqrstuvwxyz"; 24 | assert_eq!( 25 | TryRead::try_read(bytes, Str::Len(15)).unwrap(), 26 | ("abcdefghijklmno", 15) 27 | ); 28 | assert_eq!( 29 | TryRead::try_read(bytes, Str::Len(26)).unwrap(), 30 | ("abcdefghijklmnopqrstuvwxyz", 26) 31 | ); 32 | 33 | assert!(bytes.read_with::<&str>(&mut 0, Str::Len(27)).is_err()); 34 | assert!(bytes.read_with::<&str>(&mut 27, Str::Len(0)).is_err()); 35 | assert!(bytes.read_with::<&str>(&mut 26, Str::Len(1)).is_err()); 36 | } 37 | 38 | #[test] 39 | fn test_str_delimitor() { 40 | let bytes: &[u8] = b""; 41 | assert_eq!( 42 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 0)).unwrap(), 43 | ("", 0) 44 | ); 45 | 46 | let bytes: &[u8] = b"abcdefg"; 47 | assert_eq!( 48 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 6)).unwrap(), 49 | ("abcdef", 6) 50 | ); 51 | assert_eq!( 52 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 7)).unwrap(), 53 | ("abcdefg", 7) 54 | ); 55 | 56 | let bytes: &[u8] = b"\0abcdefg"; 57 | assert_eq!( 58 | TryRead::try_read(bytes, Str::Delimiter(NULL)).unwrap(), 59 | ("", 1) 60 | ); 61 | assert_eq!( 62 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 0)).unwrap(), 63 | ("", 0) 64 | ); 65 | assert_eq!( 66 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 1)).unwrap(), 67 | ("", 1) 68 | ); 69 | 70 | let bytes: &[u8] = b"abcd\0efg"; 71 | assert_eq!( 72 | TryRead::try_read(bytes, Str::Delimiter(NULL)).unwrap(), 73 | ("abcd", 5) 74 | ); 75 | assert_eq!( 76 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 4)).unwrap(), 77 | ("abcd", 4) 78 | ); 79 | assert_eq!( 80 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 5)).unwrap(), 81 | ("abcd", 5) 82 | ); 83 | assert_eq!( 84 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 6)).unwrap(), 85 | ("abcd", 5) 86 | ); 87 | 88 | let bytes: &[u8] = b"abcdefg\0"; 89 | assert_eq!( 90 | TryRead::try_read(bytes, Str::Delimiter(NULL)).unwrap(), 91 | ("abcdefg", 8) 92 | ); 93 | assert_eq!( 94 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 8)).unwrap(), 95 | ("abcdefg", 8) 96 | ); 97 | assert_eq!( 98 | TryRead::try_read(bytes, Str::DelimiterUntil(NULL, 20)).unwrap(), 99 | ("abcdefg", 8) 100 | ); 101 | 102 | let bytes: &[u8] = b""; 103 | assert!(bytes 104 | .read_with::<&str>(&mut 0, Str::Delimiter(NULL)) 105 | .is_err()); 106 | assert!(bytes 107 | .read_with::<&str>(&mut 0, Str::DelimiterUntil(NULL, 1)) 108 | .is_err()); 109 | 110 | let bytes: &[u8] = b"abcdefg"; 111 | assert!(bytes 112 | .read_with::<&str>(&mut 0, Str::DelimiterUntil(NULL, 8)) 113 | .is_err()); 114 | assert!(bytes 115 | .read_with::<&str>(&mut 0, Str::Delimiter(NULL)) 116 | .is_err()); 117 | } 118 | 119 | #[test] 120 | fn test_str_write() { 121 | let mut bytes = [0; 20]; 122 | let mut offset = 0; 123 | bytes.write(&mut offset, "hello world!").unwrap(); 124 | assert_eq!(offset, 12); 125 | assert_eq!(&bytes[..offset], b"hello world!" as &[u8]); 126 | 127 | let bytes = &mut [0; 10]; 128 | assert!(bytes.write(&mut 0, "hello world!").is_err()); 129 | } 130 | 131 | #[test] 132 | fn test_bytes() { 133 | let bytes: &[u8] = &[0xde, 0xad, 0xbe, 0xef]; 134 | assert_eq!( 135 | TryRead::try_read(&bytes, Bytes::Len(4)).unwrap(), 136 | (&bytes[..], 4) 137 | ); 138 | 139 | assert!(bytes.read_with::<&[u8]>(&mut 5, Bytes::Len(0)).is_err()); 140 | 141 | let mut write = [0; 5]; 142 | assert_eq!(TryWrite::try_write(bytes, &mut write, ()).unwrap(), 4); 143 | assert_eq!(&write[..4], bytes); 144 | 145 | assert!([0u8; 3].write(&mut 0, bytes).is_err()); 146 | } 147 | 148 | #[test] 149 | fn test_bytes_pattern() { 150 | let bytes: &[u8] = b"abcdefghijk"; 151 | 152 | assert_eq!( 153 | TryRead::try_read(bytes, Bytes::Pattern(b"abc")).unwrap(), 154 | (&b"abc"[..], 3) 155 | ); 156 | assert_eq!( 157 | TryRead::try_read(bytes, Bytes::Pattern(b"cde")).unwrap(), 158 | (&b"abcde"[..], 5) 159 | ); 160 | assert_eq!( 161 | TryRead::try_read(bytes, Bytes::Pattern(b"jk")).unwrap(), 162 | (&b"abcdefghijk"[..], 11) 163 | ); 164 | assert_eq!( 165 | TryRead::try_read(bytes, Bytes::PatternUntil(b"abc", 3)).unwrap(), 166 | (&b"abc"[..], 3) 167 | ); 168 | assert_eq!( 169 | TryRead::try_read(bytes, Bytes::PatternUntil(b"abc", 4)).unwrap(), 170 | (&b"abc"[..], 3) 171 | ); 172 | assert_eq!( 173 | TryRead::try_read(bytes, Bytes::PatternUntil(b"cde", 3)).unwrap(), 174 | (&b"abc"[..], 3) 175 | ); 176 | assert_eq!( 177 | TryRead::try_read(bytes, Bytes::PatternUntil(b"cde", 4)).unwrap(), 178 | (&b"abcd"[..], 4) 179 | ); 180 | assert_eq!( 181 | TryRead::try_read(bytes, Bytes::PatternUntil(b"cde", 5)).unwrap(), 182 | (&b"abcde"[..], 5) 183 | ); 184 | assert_eq!( 185 | TryRead::try_read(bytes, Bytes::PatternUntil(b"cde", 6)).unwrap(), 186 | (&b"abcde"[..], 5) 187 | ); 188 | assert_eq!( 189 | TryRead::try_read(bytes, Bytes::PatternUntil(b"xyz", 5)).unwrap(), 190 | (&b"abcde"[..], 5) 191 | ); 192 | assert!(bytes 193 | .read_with::<&[u8]>(&mut 0, Bytes::Pattern(b"xyz")) 194 | .is_err()); 195 | assert!(bytes 196 | .read_with::<&[u8]>(&mut 0, Bytes::Pattern(b"")) 197 | .is_err()); 198 | assert!(bytes 199 | .read_with::<&[u8]>(&mut 0, Bytes::PatternUntil(b"", 3)) 200 | .is_err()); 201 | assert!(bytes 202 | .read_with::<&[u8]>(&mut 0, Bytes::PatternUntil(b"abcd", 3)) 203 | .is_err()); 204 | assert!(bytes 205 | .read_with::<&[u8]>(&mut 0, Bytes::PatternUntil(b"xyz", 20)) 206 | .is_err()); 207 | 208 | let bytes: &[u8] = b""; 209 | assert!(bytes 210 | .read_with::<&[u8]>(&mut 0, Bytes::Pattern(b"xyz")) 211 | .is_err()); 212 | assert!(bytes 213 | .read_with::<&[u8]>(&mut 0, Bytes::PatternUntil(b"abc", 3)) 214 | .is_err()); 215 | assert!(bytes 216 | .read_with::<&[u8]>(&mut 0, Bytes::PatternUntil(b"abc", 4)) 217 | .is_err()); 218 | } 219 | 220 | #[test] 221 | fn test_bool() { 222 | let bytes = [0x00, 0x01, 0x80, 0xff]; 223 | assert_eq!(bytes.read::(&mut 0).unwrap(), false); 224 | assert_eq!(bytes.read::(&mut 1).unwrap(), true); 225 | assert_eq!(bytes.read::(&mut 2).unwrap(), true); 226 | assert_eq!(bytes.read::(&mut 3).unwrap(), true); 227 | 228 | let mut bytes = [0u8; 2]; 229 | bytes.write(&mut 0, false).unwrap(); 230 | bytes.write(&mut 1, true).unwrap(); 231 | assert!(bytes[0] == 0); 232 | assert!(bytes[1] != 0); 233 | } 234 | 235 | #[test] 236 | fn test_iter() { 237 | let bytes: &[u8] = b"hello\0world\0dead\0beef\0more"; 238 | let mut offset = 0; 239 | { 240 | let mut iter = bytes.read_iter(&mut offset, Str::Delimiter(NULL)); 241 | assert_eq!(iter.next(), Some("hello")); 242 | assert_eq!(iter.next(), Some("world")); 243 | assert_eq!(iter.next(), Some("dead")); 244 | assert_eq!(iter.next(), Some("beef")); 245 | assert_eq!(iter.next(), None); 246 | } 247 | assert_eq!(offset, 22); 248 | } 249 | 250 | macro_rules! test_num { 251 | ($test_name: tt, $ty: ty, $byteorder_read_fn: tt, $byteorder_write_fn: tt) => { 252 | quickcheck! { 253 | fn $test_name (num: $ty) -> () { 254 | let mut bytes = [0u8; 8]; 255 | bytes.write_with(&mut 0, num, LE).unwrap(); 256 | let result = LittleEndian::$byteorder_read_fn(&bytes); 257 | assert_eq!(result, num); 258 | 259 | let mut bytes = [0u8; 8]; 260 | LittleEndian::$byteorder_write_fn(&mut bytes, num); 261 | let result: $ty = bytes.read_with(&mut 0, LE).unwrap(); 262 | assert_eq!(result, num); 263 | 264 | let mut bytes = [0u8; 8]; 265 | bytes.write_with(&mut 0, num, BE).unwrap(); 266 | let result = BigEndian::$byteorder_read_fn(&bytes); 267 | assert_eq!(result, num); 268 | 269 | let mut bytes = [0u8; 8]; 270 | BigEndian::$byteorder_write_fn(&mut bytes, num); 271 | let result: $ty = bytes.read_with(&mut 0, BE).unwrap(); 272 | assert_eq!(result, num); 273 | } 274 | } 275 | }; 276 | } 277 | 278 | test_num!(test_u16, u16, read_u16, write_u16); 279 | test_num!(test_u32, u32, read_u32, write_u32); 280 | test_num!(test_u64, u64, read_u64, write_u64); 281 | test_num!(test_i16, i16, read_i16, write_i16); 282 | test_num!(test_i32, i32, read_i32, write_i32); 283 | test_num!(test_i64, i64, read_i64, write_i64); 284 | test_num!(test_f32, f32, read_f32, write_f32); 285 | test_num!(test_f64, f64, read_f64, write_f64); 286 | 287 | struct Header<'a> { 288 | name: &'a str, 289 | enabled: bool, 290 | } 291 | 292 | impl<'a> TryRead<'a, Endian> for Header<'a> { 293 | fn try_read(bytes: &'a [u8], endian: Endian) -> Result<(Self, usize)> { 294 | let offset = &mut 0; 295 | 296 | let name_len = bytes.read_with::(offset, endian)? as usize; 297 | let header = Header { 298 | name: bytes.read_with::<&str>(offset, Str::Len(name_len))?, 299 | enabled: bytes.read(offset)?, 300 | }; 301 | 302 | Ok((header, *offset)) 303 | } 304 | } 305 | 306 | impl<'a> TryWrite for Header<'a> { 307 | fn try_write(self, bytes: &mut [u8], endian: Endian) -> Result { 308 | let offset = &mut 0; 309 | 310 | bytes.write_with(offset, self.name.len() as u16, endian)?; 311 | bytes.write(offset, self.name)?; 312 | bytes.write(offset, self.enabled)?; 313 | 314 | Ok(*offset) 315 | } 316 | } 317 | 318 | #[test] 319 | fn test_api() { 320 | let bytes = [0, 5, b"H"[0], b"E"[0], b"L"[0], b"L"[0], b"O"[0], 0]; 321 | 322 | let header: Header = bytes.read_with(&mut 0, BE).unwrap(); 323 | assert_eq!(header.name, "HELLO"); 324 | assert_eq!(header.enabled, false); 325 | 326 | let mut write = [0u8; 8]; 327 | write.write_with(&mut 0, header, BE).unwrap(); 328 | assert_eq!(write, bytes); 329 | } 330 | 331 | #[derive(Debug, Clone, PartialEq, Eq)] 332 | struct Empty; 333 | 334 | impl<'a> TryRead<'a, ()> for Empty { 335 | fn try_read(bytes: &'a [u8], _ctx: ()) -> Result<(Self, usize)> { 336 | Ok((Self, 0)) 337 | } 338 | } 339 | 340 | impl TryWrite<()> for Empty { 341 | fn try_write(self, bytes: &mut [u8], _ctx: ()) -> Result { 342 | Ok(0) 343 | } 344 | } 345 | 346 | #[test] 347 | fn test_empty() { 348 | let empty_bytes: [u8; 0] = []; 349 | let mut offset = 0; 350 | let empty: Empty = empty_bytes.read(&mut offset).unwrap(); 351 | assert_eq!(empty, Empty); 352 | assert_eq!(offset, 0); 353 | 354 | let zero_bytes = [0; 8]; 355 | let mut offset = 0; 356 | let empty: Empty = zero_bytes.read(&mut offset).unwrap(); 357 | assert_eq!(empty, Empty); 358 | assert_eq!(offset, 0); 359 | 360 | let mut write_empty_bytes: [u8; 0] = []; 361 | let mut offset = 0; 362 | write_empty_bytes.write(&mut offset, Empty).unwrap(); 363 | assert_eq!(write_empty_bytes, empty_bytes); 364 | assert_eq!(offset, 0); 365 | 366 | let mut write_zero_bytes = [0u8; 4]; 367 | let mut offset = 0; 368 | write_zero_bytes.write(&mut offset, Empty).unwrap(); 369 | assert_eq!(write_zero_bytes, [0u8; 4]); 370 | assert_eq!(offset, 0); 371 | } 372 | --------------------------------------------------------------------------------