Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move Bytes to read::util #336

Merged
merged 1 commit into from
Jul 25, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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")]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this also needs to be enabled for write_core.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, I didn't see any uses of alloc based on git grep alloc src/write/

#[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