Skip to content

Commit

Permalink
Move Bytes to read::util
Browse files Browse the repository at this point in the history
This also means we don't need `alloc` when no features are enabled.
  • Loading branch information
philipc committed Jul 25, 2021
1 parent 5a3bf7e commit 2f9cab9
Show file tree
Hide file tree
Showing 11 changed files with 334 additions and 326 deletions.
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
307 changes: 1 addition & 306 deletions src/pod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
allow(dead_code)
)]

use alloc::string::String;
use core::{fmt, mem, result, slice};
use core::{mem, result, slice};

type Result<T> = result::Result<T, ()>;

Expand Down Expand Up @@ -146,207 +145,6 @@ pub fn bytes_of_slice_mut<T: Pod>(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<Bytes<'data>> {
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<Bytes<'data>> {
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<T: Pod>(&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<T: Pod>(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<T: Pod>(&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<T: Pod>(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),+ $(,)?) => {
$(
Expand Down Expand Up @@ -437,107 +235,4 @@ mod tests {
assert_eq!(slice_from_bytes_mut::<u16>(&mut bytes_mut[2..], 4), Err(()));
assert_eq!(slice_from_bytes_mut::<u16>(&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::<u16>(), Ok(&u16::to_be(0x0123)));
assert_eq!(bytes, Bytes(&[0x45, 0x67]));
assert_eq!(data.read_at::<u16>(2), Ok(&u16::to_be(0x4567)));
assert_eq!(data.read_at::<u16>(3), Err(()));
assert_eq!(data.read_at::<u16>(4), Err(()));

let mut bytes = data;
assert_eq!(bytes.read::<u32>(), Ok(&x));
assert_eq!(bytes, Bytes(&[]));

let mut bytes = data;
assert_eq!(bytes.read::<u64>(), Err(()));
assert_eq!(bytes, Bytes(&[]));

let mut bytes = data;
assert_eq!(bytes.read_slice::<u8>(0), Ok(&[][..]));
assert_eq!(bytes, data);

let mut bytes = data;
assert_eq!(bytes.read_slice::<u8>(4), Ok(data.0));
assert_eq!(bytes, Bytes(&[]));

let mut bytes = data;
assert_eq!(bytes.read_slice::<u8>(5), Err(()));
assert_eq!(bytes, Bytes(&[]));

assert_eq!(data.read_slice_at::<u8>(0, 0), Ok(&[][..]));
assert_eq!(data.read_slice_at::<u8>(4, 0), Ok(&[][..]));
assert_eq!(data.read_slice_at::<u8>(0, 4), Ok(data.0));
assert_eq!(data.read_slice_at::<u8>(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]"
);
}
}
6 changes: 3 additions & 3 deletions src/read/coff/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 3 additions & 3 deletions src/read/elf/file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
4 changes: 2 additions & 2 deletions src/read/elf/note.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
6 changes: 3 additions & 3 deletions src/read/elf/section.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down
4 changes: 2 additions & 2 deletions src/read/elf/segment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down
Loading

0 comments on commit 2f9cab9

Please sign in to comment.