From da51e9addda65e2df88d72a1f317e119314e933e Mon Sep 17 00:00:00 2001 From: Philip Craig Date: Sun, 25 Jul 2021 00:00:00 +0000 Subject: [PATCH] Move `Bytes` to `read::util` (#336) This also means we don't need `alloc` when no features are enabled. --- src/lib.rs | 1 + src/pod.rs | 307 +------------------------------- src/read/coff/symbol.rs | 6 +- src/read/elf/file.rs | 6 +- src/read/elf/note.rs | 4 +- src/read/elf/section.rs | 6 +- src/read/elf/segment.rs | 4 +- src/read/elf/version.rs | 3 +- src/read/macho/load_command.rs | 4 +- src/read/mod.rs | 3 +- src/read/util.rs | 316 ++++++++++++++++++++++++++++++++- 11 files changed, 334 insertions(+), 326 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 93d58b0b..e62ad5ce 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,7 @@ #[cfg(feature = "cargo-all")] compile_error!("'--all-features' is not supported; use '--features all' instead"); +#[cfg(feature = "read_core")] #[allow(unused_imports)] #[macro_use] extern crate alloc; diff --git a/src/pod.rs b/src/pod.rs index 119675b0..805cf824 100644 --- a/src/pod.rs +++ b/src/pod.rs @@ -8,8 +8,7 @@ allow(dead_code) )] -use alloc::string::String; -use core::{fmt, mem, result, slice}; +use core::{mem, result, slice}; type Result = result::Result; @@ -146,207 +145,6 @@ pub fn bytes_of_slice_mut(val: &mut [T]) -> &mut [u8] { unsafe { slice::from_raw_parts_mut(val.as_mut_ptr().cast(), size) } } -/// A newtype for byte slices. -/// -/// It has these important features: -/// - no methods that can panic, such as `Index` -/// - convenience methods for `Pod` types -/// - a useful `Debug` implementation -#[derive(Default, Clone, Copy, PartialEq, Eq)] -pub struct Bytes<'data>(pub &'data [u8]); - -impl<'data> fmt::Debug for Bytes<'data> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - debug_list_bytes(self.0, fmt) - } -} - -impl<'data> Bytes<'data> { - /// Return the length of the byte slice. - #[inline] - pub fn len(&self) -> usize { - self.0.len() - } - - /// Return true if the byte slice is empty. - #[inline] - pub fn is_empty(&self) -> bool { - self.0.is_empty() - } - - /// Skip over the given number of bytes at the start of the byte slice. - /// - /// Modifies the byte slice to start after the bytes. - /// - /// Returns an error if there are too few bytes. - #[inline] - pub fn skip(&mut self, offset: usize) -> Result<()> { - match self.0.get(offset..) { - Some(tail) => { - self.0 = tail; - Ok(()) - } - None => { - self.0 = &[]; - Err(()) - } - } - } - - /// Return a reference to the given number of bytes at the start of the byte slice. - /// - /// Modifies the byte slice to start after the bytes. - /// - /// Returns an error if there are too few bytes. - #[inline] - pub fn read_bytes(&mut self, count: usize) -> Result> { - match (self.0.get(..count), self.0.get(count..)) { - (Some(head), Some(tail)) => { - self.0 = tail; - Ok(Bytes(head)) - } - _ => { - self.0 = &[]; - Err(()) - } - } - } - - /// Return a reference to the given number of bytes at the given offset of the byte slice. - /// - /// Returns an error if the offset is invalid or there are too few bytes. - #[inline] - pub fn read_bytes_at(mut self, offset: usize, count: usize) -> Result> { - self.skip(offset)?; - self.read_bytes(count) - } - - /// Return a reference to a `Pod` struct at the start of the byte slice. - /// - /// Modifies the byte slice to start after the bytes. - /// - /// Returns an error if there are too few bytes or the slice is incorrectly aligned. - #[inline] - pub fn read(&mut self) -> Result<&'data T> { - match from_bytes(self.0) { - Ok((value, tail)) => { - self.0 = tail; - Ok(value) - } - Err(()) => { - self.0 = &[]; - Err(()) - } - } - } - - /// Return a reference to a `Pod` struct at the given offset of the byte slice. - /// - /// Returns an error if there are too few bytes or the offset is incorrectly aligned. - #[inline] - pub fn read_at(mut self, offset: usize) -> Result<&'data T> { - self.skip(offset)?; - self.read() - } - - /// Return a reference to a slice of `Pod` structs at the start of the byte slice. - /// - /// Modifies the byte slice to start after the bytes. - /// - /// Returns an error if there are too few bytes or the offset is incorrectly aligned. - #[inline] - pub fn read_slice(&mut self, count: usize) -> Result<&'data [T]> { - match slice_from_bytes(self.0, count) { - Ok((value, tail)) => { - self.0 = tail; - Ok(value) - } - Err(()) => { - self.0 = &[]; - Err(()) - } - } - } - - /// Return a reference to a slice of `Pod` structs at the given offset of the byte slice. - /// - /// Returns an error if there are too few bytes or the offset is incorrectly aligned. - #[inline] - pub fn read_slice_at(mut self, offset: usize, count: usize) -> Result<&'data [T]> { - self.skip(offset)?; - self.read_slice(count) - } - - /// Read a null terminated string. - /// - /// Does not assume any encoding. - /// Reads past the null byte, but doesn't return it. - #[inline] - pub fn read_string(&mut self) -> Result<&'data [u8]> { - match memchr::memchr(b'\0', self.0) { - Some(null) => { - // These will never fail. - let bytes = self.read_bytes(null)?; - self.skip(1)?; - Ok(bytes.0) - } - None => { - self.0 = &[]; - Err(()) - } - } - } - - /// Read a null terminated string at an offset. - /// - /// Does not assume any encoding. Does not return the null byte. - #[inline] - pub fn read_string_at(mut self, offset: usize) -> Result<&'data [u8]> { - self.skip(offset)?; - self.read_string() - } -} - -// Only for Debug impl of `Bytes`. -fn debug_list_bytes(bytes: &[u8], fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - let mut list = fmt.debug_list(); - list.entries(bytes.iter().take(8).copied().map(DebugByte)); - if bytes.len() > 8 { - list.entry(&DebugLen(bytes.len())); - } - list.finish() -} - -struct DebugByte(u8); - -impl fmt::Debug for DebugByte { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "0x{:02x}", self.0) - } -} - -struct DebugLen(usize); - -impl fmt::Debug for DebugLen { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "...; {}", self.0) - } -} - -/// A newtype for byte strings. -/// -/// For byte slices that are strings of an unknown encoding. -/// -/// Provides a `Debug` implementation that interprets the bytes as UTF-8. -#[derive(Default, Clone, Copy, PartialEq, Eq)] -pub(crate) struct ByteString<'data>(pub &'data [u8]); - -impl<'data> fmt::Debug for ByteString<'data> { - fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(fmt, "\"{}\"", String::from_utf8_lossy(self.0)) - } -} - macro_rules! unsafe_impl_pod { ($($struct_name:ident),+ $(,)?) => { $( @@ -437,107 +235,4 @@ mod tests { assert_eq!(slice_from_bytes_mut::(&mut bytes_mut[2..], 4), Err(())); assert_eq!(slice_from_bytes_mut::(&mut bytes_mut[1..], 2), Err(())); } - - #[test] - fn bytes() { - let x = u32::to_be(0x0123_4567); - let data = Bytes(bytes_of(&x)); - - let mut bytes = data; - assert_eq!(bytes.skip(0), Ok(())); - assert_eq!(bytes, data); - - let mut bytes = data; - assert_eq!(bytes.skip(4), Ok(())); - assert_eq!(bytes, Bytes(&[])); - - let mut bytes = data; - assert_eq!(bytes.skip(5), Err(())); - assert_eq!(bytes, Bytes(&[])); - - let mut bytes = data; - assert_eq!(bytes.read_bytes(0), Ok(Bytes(&[]))); - assert_eq!(bytes, data); - - let mut bytes = data; - assert_eq!(bytes.read_bytes(4), Ok(data)); - assert_eq!(bytes, Bytes(&[])); - - let mut bytes = data; - assert_eq!(bytes.read_bytes(5), Err(())); - assert_eq!(bytes, Bytes(&[])); - - assert_eq!(data.read_bytes_at(0, 0), Ok(Bytes(&[]))); - assert_eq!(data.read_bytes_at(4, 0), Ok(Bytes(&[]))); - assert_eq!(data.read_bytes_at(0, 4), Ok(data)); - assert_eq!(data.read_bytes_at(1, 4), Err(())); - - let mut bytes = data; - assert_eq!(bytes.read::(), Ok(&u16::to_be(0x0123))); - assert_eq!(bytes, Bytes(&[0x45, 0x67])); - assert_eq!(data.read_at::(2), Ok(&u16::to_be(0x4567))); - assert_eq!(data.read_at::(3), Err(())); - assert_eq!(data.read_at::(4), Err(())); - - let mut bytes = data; - assert_eq!(bytes.read::(), Ok(&x)); - assert_eq!(bytes, Bytes(&[])); - - let mut bytes = data; - assert_eq!(bytes.read::(), Err(())); - assert_eq!(bytes, Bytes(&[])); - - let mut bytes = data; - assert_eq!(bytes.read_slice::(0), Ok(&[][..])); - assert_eq!(bytes, data); - - let mut bytes = data; - assert_eq!(bytes.read_slice::(4), Ok(data.0)); - assert_eq!(bytes, Bytes(&[])); - - let mut bytes = data; - assert_eq!(bytes.read_slice::(5), Err(())); - assert_eq!(bytes, Bytes(&[])); - - assert_eq!(data.read_slice_at::(0, 0), Ok(&[][..])); - assert_eq!(data.read_slice_at::(4, 0), Ok(&[][..])); - assert_eq!(data.read_slice_at::(0, 4), Ok(data.0)); - assert_eq!(data.read_slice_at::(1, 4), Err(())); - - let data = Bytes(&[0x01, 0x02, 0x00, 0x04]); - - let mut bytes = data; - assert_eq!(bytes.read_string(), Ok(&data.0[..2])); - assert_eq!(bytes.0, &data.0[3..]); - - let mut bytes = data; - bytes.skip(3).unwrap(); - assert_eq!(bytes.read_string(), Err(())); - assert_eq!(bytes.0, &[]); - - assert_eq!(data.read_string_at(0), Ok(&data.0[..2])); - assert_eq!(data.read_string_at(1), Ok(&data.0[1..2])); - assert_eq!(data.read_string_at(2), Ok(&[][..])); - assert_eq!(data.read_string_at(3), Err(())); - } - - #[test] - fn bytes_debug() { - assert_eq!(format!("{:?}", Bytes(&[])), "[]"); - assert_eq!(format!("{:?}", Bytes(&[0x01])), "[0x01]"); - assert_eq!( - format!( - "{:?}", - Bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]) - ), - "[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]" - ); - assert_eq!( - format!( - "{:?}", - Bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]) - ), - "[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ...; 9]" - ); - } } diff --git a/src/read/coff/symbol.rs b/src/read/coff/symbol.rs index 49ee2fc4..5af801f1 100644 --- a/src/read/coff/symbol.rs +++ b/src/read/coff/symbol.rs @@ -6,11 +6,11 @@ use core::str; use super::{CoffCommon, SectionTable}; use crate::endian::{LittleEndian as LE, U32Bytes}; use crate::pe; -use crate::pod::{bytes_of_slice, Bytes, Pod}; +use crate::pod::{bytes_of_slice, Pod}; use crate::read::util::StringTable; use crate::read::{ - self, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, SymbolFlags, - SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, + self, Bytes, ObjectSymbol, ObjectSymbolTable, ReadError, ReadRef, Result, SectionIndex, + SymbolFlags, SymbolIndex, SymbolKind, SymbolMap, SymbolMapEntry, SymbolScope, SymbolSection, }; /// A table of symbol entries in a COFF or PE file. diff --git a/src/read/elf/file.rs b/src/read/elf/file.rs index 75f06579..91273ca4 100644 --- a/src/read/elf/file.rs +++ b/src/read/elf/file.rs @@ -4,10 +4,10 @@ use core::fmt::Debug; use core::{mem, str}; use crate::read::{ - self, util, Architecture, Error, Export, FileFlags, Import, Object, ReadError, ReadRef, - SectionIndex, StringTable, SymbolIndex, + self, util, Architecture, ByteString, Bytes, Error, Export, FileFlags, Import, Object, + ReadError, ReadRef, SectionIndex, StringTable, SymbolIndex, }; -use crate::{elf, endian, ByteString, Bytes, Endian, Endianness, Pod, U32}; +use crate::{elf, endian, Endian, Endianness, Pod, U32}; use super::{ CompressionHeader, Dyn, ElfComdat, ElfComdatIterator, ElfDynamicRelocationIterator, ElfSection, diff --git a/src/read/elf/note.rs b/src/read/elf/note.rs index 8f5cb5ac..34024dbb 100644 --- a/src/read/elf/note.rs +++ b/src/read/elf/note.rs @@ -3,9 +3,9 @@ use core::mem; use crate::elf; use crate::endian; -use crate::pod::{Bytes, Pod}; +use crate::pod::Pod; use crate::read::util; -use crate::read::{self, Error, ReadError}; +use crate::read::{self, Bytes, Error, ReadError}; use super::FileHeader; diff --git a/src/read/elf/section.rs b/src/read/elf/section.rs index 69e33d11..5d899922 100644 --- a/src/read/elf/section.rs +++ b/src/read/elf/section.rs @@ -3,10 +3,10 @@ use core::{iter, mem, slice, str}; use crate::elf; use crate::endian::{self, Endianness, U32Bytes}; -use crate::pod::{Bytes, Pod}; +use crate::pod::Pod; use crate::read::{ - self, CompressedData, CompressedFileRange, CompressionFormat, Error, ObjectSection, ReadError, - ReadRef, SectionFlags, SectionIndex, SectionKind, StringTable, + self, Bytes, CompressedData, CompressedFileRange, CompressionFormat, Error, ObjectSection, + ReadError, ReadRef, SectionFlags, SectionIndex, SectionKind, StringTable, }; use super::{ diff --git a/src/read/elf/segment.rs b/src/read/elf/segment.rs index b75b3934..70528b59 100644 --- a/src/read/elf/segment.rs +++ b/src/read/elf/segment.rs @@ -3,8 +3,8 @@ use core::{mem, slice, str}; use crate::elf; use crate::endian::{self, Endianness}; -use crate::pod::{Bytes, Pod}; -use crate::read::{self, ObjectSegment, ReadError, ReadRef}; +use crate::pod::Pod; +use crate::read::{self, Bytes, ObjectSegment, ReadError, ReadRef}; use super::{ElfFile, FileHeader, NoteIterator}; diff --git a/src/read/elf/version.rs b/src/read/elf/version.rs index b3934910..099e27af 100644 --- a/src/read/elf/version.rs +++ b/src/read/elf/version.rs @@ -1,6 +1,5 @@ use crate::elf; -use crate::pod::Bytes; -use crate::read::{ReadError, Result}; +use crate::read::{Bytes, ReadError, Result}; use super::FileHeader; diff --git a/src/read/macho/load_command.rs b/src/read/macho/load_command.rs index d7b3041a..29fab6e0 100644 --- a/src/read/macho/load_command.rs +++ b/src/read/macho/load_command.rs @@ -2,9 +2,9 @@ use core::marker::PhantomData; use crate::endian::Endian; use crate::macho; -use crate::pod::{Bytes, Pod}; +use crate::pod::Pod; use crate::read::macho::{MachHeader, SymbolTable}; -use crate::read::{ReadError, ReadRef, Result, StringTable}; +use crate::read::{Bytes, ReadError, ReadRef, Result, StringTable}; /// An iterator over the load commands of a `MachHeader`. #[derive(Debug, Default, Clone, Copy)] diff --git a/src/read/mod.rs b/src/read/mod.rs index 0ff9aece..75814eef 100644 --- a/src/read/mod.rs +++ b/src/read/mod.rs @@ -5,7 +5,6 @@ use alloc::vec::Vec; use core::{fmt, result}; use crate::common::*; -use crate::ByteString; mod read_ref; pub use read_ref::*; @@ -16,7 +15,7 @@ mod read_cache; pub use read_cache::*; mod util; -pub use util::StringTable; +pub use util::*; #[cfg(any( feature = "coff", diff --git a/src/read/util.rs b/src/read/util.rs index e851b7a2..842bd6ca 100644 --- a/src/read/util.rs +++ b/src/read/util.rs @@ -1,7 +1,212 @@ -use core::{convert::TryInto, marker::PhantomData}; +use alloc::string::String; +use core::convert::TryInto; +use core::fmt; +use core::marker::PhantomData; +use crate::pod::{from_bytes, slice_from_bytes, Pod}; use crate::ReadRef; +/// A newtype for byte slices. +/// +/// It has these important features: +/// - no methods that can panic, such as `Index` +/// - convenience methods for `Pod` types +/// - a useful `Debug` implementation +#[derive(Default, Clone, Copy, PartialEq, Eq)] +pub struct Bytes<'data>(pub &'data [u8]); + +impl<'data> fmt::Debug for Bytes<'data> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + debug_list_bytes(self.0, fmt) + } +} + +impl<'data> Bytes<'data> { + /// Return the length of the byte slice. + #[inline] + pub fn len(&self) -> usize { + self.0.len() + } + + /// Return true if the byte slice is empty. + #[inline] + pub fn is_empty(&self) -> bool { + self.0.is_empty() + } + + /// Skip over the given number of bytes at the start of the byte slice. + /// + /// Modifies the byte slice to start after the bytes. + /// + /// Returns an error if there are too few bytes. + #[inline] + pub fn skip(&mut self, offset: usize) -> Result<(), ()> { + match self.0.get(offset..) { + Some(tail) => { + self.0 = tail; + Ok(()) + } + None => { + self.0 = &[]; + Err(()) + } + } + } + + /// Return a reference to the given number of bytes at the start of the byte slice. + /// + /// Modifies the byte slice to start after the bytes. + /// + /// Returns an error if there are too few bytes. + #[inline] + pub fn read_bytes(&mut self, count: usize) -> Result, ()> { + match (self.0.get(..count), self.0.get(count..)) { + (Some(head), Some(tail)) => { + self.0 = tail; + Ok(Bytes(head)) + } + _ => { + self.0 = &[]; + Err(()) + } + } + } + + /// Return a reference to the given number of bytes at the given offset of the byte slice. + /// + /// Returns an error if the offset is invalid or there are too few bytes. + #[inline] + pub fn read_bytes_at(mut self, offset: usize, count: usize) -> Result, ()> { + self.skip(offset)?; + self.read_bytes(count) + } + + /// Return a reference to a `Pod` struct at the start of the byte slice. + /// + /// Modifies the byte slice to start after the bytes. + /// + /// Returns an error if there are too few bytes or the slice is incorrectly aligned. + #[inline] + pub fn read(&mut self) -> Result<&'data T, ()> { + match from_bytes(self.0) { + Ok((value, tail)) => { + self.0 = tail; + Ok(value) + } + Err(()) => { + self.0 = &[]; + Err(()) + } + } + } + + /// Return a reference to a `Pod` struct at the given offset of the byte slice. + /// + /// Returns an error if there are too few bytes or the offset is incorrectly aligned. + #[inline] + pub fn read_at(mut self, offset: usize) -> Result<&'data T, ()> { + self.skip(offset)?; + self.read() + } + + /// Return a reference to a slice of `Pod` structs at the start of the byte slice. + /// + /// Modifies the byte slice to start after the bytes. + /// + /// Returns an error if there are too few bytes or the offset is incorrectly aligned. + #[inline] + pub fn read_slice(&mut self, count: usize) -> Result<&'data [T], ()> { + match slice_from_bytes(self.0, count) { + Ok((value, tail)) => { + self.0 = tail; + Ok(value) + } + Err(()) => { + self.0 = &[]; + Err(()) + } + } + } + + /// Return a reference to a slice of `Pod` structs at the given offset of the byte slice. + /// + /// Returns an error if there are too few bytes or the offset is incorrectly aligned. + #[inline] + pub fn read_slice_at(mut self, offset: usize, count: usize) -> Result<&'data [T], ()> { + self.skip(offset)?; + self.read_slice(count) + } + + /// Read a null terminated string. + /// + /// Does not assume any encoding. + /// Reads past the null byte, but doesn't return it. + #[inline] + pub fn read_string(&mut self) -> Result<&'data [u8], ()> { + match memchr::memchr(b'\0', self.0) { + Some(null) => { + // These will never fail. + let bytes = self.read_bytes(null)?; + self.skip(1)?; + Ok(bytes.0) + } + None => { + self.0 = &[]; + Err(()) + } + } + } + + /// Read a null terminated string at an offset. + /// + /// Does not assume any encoding. Does not return the null byte. + #[inline] + pub fn read_string_at(mut self, offset: usize) -> Result<&'data [u8], ()> { + self.skip(offset)?; + self.read_string() + } +} + +// Only for Debug impl of `Bytes`. +fn debug_list_bytes(bytes: &[u8], fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut list = fmt.debug_list(); + list.entries(bytes.iter().take(8).copied().map(DebugByte)); + if bytes.len() > 8 { + list.entry(&DebugLen(bytes.len())); + } + list.finish() +} + +struct DebugByte(u8); + +impl fmt::Debug for DebugByte { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "0x{:02x}", self.0) + } +} + +struct DebugLen(usize); + +impl fmt::Debug for DebugLen { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "...; {}", self.0) + } +} + +/// A newtype for byte strings. +/// +/// For byte slices that are strings of an unknown encoding. +/// +/// Provides a `Debug` implementation that interprets the bytes as UTF-8. +#[derive(Default, Clone, Copy, PartialEq, Eq)] +pub(crate) struct ByteString<'data>(pub &'data [u8]); + +impl<'data> fmt::Debug for ByteString<'data> { + fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(fmt, "\"{}\"", String::from_utf8_lossy(self.0)) + } +} + #[allow(dead_code)] #[inline] pub(crate) fn align(offset: usize, size: usize) -> usize { @@ -67,3 +272,112 @@ impl<'data, R: ReadRef<'data>> Default for StringTable<'data, R> { } } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::pod::bytes_of; + + #[test] + fn bytes() { + let x = u32::to_be(0x0123_4567); + let data = Bytes(bytes_of(&x)); + + let mut bytes = data; + assert_eq!(bytes.skip(0), Ok(())); + assert_eq!(bytes, data); + + let mut bytes = data; + assert_eq!(bytes.skip(4), Ok(())); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.skip(5), Err(())); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_bytes(0), Ok(Bytes(&[]))); + assert_eq!(bytes, data); + + let mut bytes = data; + assert_eq!(bytes.read_bytes(4), Ok(data)); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_bytes(5), Err(())); + assert_eq!(bytes, Bytes(&[])); + + assert_eq!(data.read_bytes_at(0, 0), Ok(Bytes(&[]))); + assert_eq!(data.read_bytes_at(4, 0), Ok(Bytes(&[]))); + assert_eq!(data.read_bytes_at(0, 4), Ok(data)); + assert_eq!(data.read_bytes_at(1, 4), Err(())); + + let mut bytes = data; + assert_eq!(bytes.read::(), Ok(&u16::to_be(0x0123))); + assert_eq!(bytes, Bytes(&[0x45, 0x67])); + assert_eq!(data.read_at::(2), Ok(&u16::to_be(0x4567))); + assert_eq!(data.read_at::(3), Err(())); + assert_eq!(data.read_at::(4), Err(())); + + let mut bytes = data; + assert_eq!(bytes.read::(), Ok(&x)); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read::(), Err(())); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_slice::(0), Ok(&[][..])); + assert_eq!(bytes, data); + + let mut bytes = data; + assert_eq!(bytes.read_slice::(4), Ok(data.0)); + assert_eq!(bytes, Bytes(&[])); + + let mut bytes = data; + assert_eq!(bytes.read_slice::(5), Err(())); + assert_eq!(bytes, Bytes(&[])); + + assert_eq!(data.read_slice_at::(0, 0), Ok(&[][..])); + assert_eq!(data.read_slice_at::(4, 0), Ok(&[][..])); + assert_eq!(data.read_slice_at::(0, 4), Ok(data.0)); + assert_eq!(data.read_slice_at::(1, 4), Err(())); + + let data = Bytes(&[0x01, 0x02, 0x00, 0x04]); + + let mut bytes = data; + assert_eq!(bytes.read_string(), Ok(&data.0[..2])); + assert_eq!(bytes.0, &data.0[3..]); + + let mut bytes = data; + bytes.skip(3).unwrap(); + assert_eq!(bytes.read_string(), Err(())); + assert_eq!(bytes.0, &[]); + + assert_eq!(data.read_string_at(0), Ok(&data.0[..2])); + assert_eq!(data.read_string_at(1), Ok(&data.0[1..2])); + assert_eq!(data.read_string_at(2), Ok(&[][..])); + assert_eq!(data.read_string_at(3), Err(())); + } + + #[test] + fn bytes_debug() { + assert_eq!(format!("{:?}", Bytes(&[])), "[]"); + assert_eq!(format!("{:?}", Bytes(&[0x01])), "[0x01]"); + assert_eq!( + format!( + "{:?}", + Bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]) + ), + "[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08]" + ); + assert_eq!( + format!( + "{:?}", + Bytes(&[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]) + ), + "[0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, ...; 9]" + ); + } +}