use std::borrow::Cow;
use std::fmt;
use std::io;
use std::mem;
use std::ops::{Add, AddAssign, Sub};
use std::result;
use std::slice;
use scroll::ctx::TryFromCtx;
use scroll::{self, Endian, Pread, LE};
use crate::tpi::constants;
#[non_exhaustive]
#[derive(Debug)]
pub enum Error {
UnrecognizedFileFormat,
InvalidPageSize(u32),
PageReferenceOutOfRange(u32),
StreamNotFound(u32),
StreamNameNotFound,
InvalidStreamLength(&'static str),
IoError(io::Error),
UnexpectedEof,
UnimplementedFeature(&'static str),
GlobalSymbolsNotFound,
SymbolTooShort,
UnimplementedSymbolKind(u16),
InvalidTypeInformationHeader(&'static str),
TypeTooShort,
TypeNotFound(u32),
TypeNotIndexed(u32, u32),
UnimplementedTypeKind(u16),
NotACrossModuleRef(u32),
CrossModuleRefNotFound(u32),
UnexpectedNumericPrefix(u16),
AddressMapNotFound,
ScrollError(scroll::Error),
UnimplementedDebugSubsection(u32),
UnimplementedFileChecksumKind(u8),
InvalidFileChecksumOffset(u32),
LinesNotFound,
InvalidCompressedAnnotation,
UnknownBinaryAnnotation(u32),
}
impl std::error::Error for Error {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::IoError(error) => Some(error),
_ => None,
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
match self {
Self::PageReferenceOutOfRange(p) => {
write!(f, "MSF referred to page number ({}) out of range", p)
}
Self::InvalidPageSize(n) => write!(
f,
"The MSF header specifies an invalid page size ({} bytes)",
n
),
Self::StreamNotFound(s) => {
write!(f, "The requested stream ({}) is not stored in this file", s)
}
Self::InvalidStreamLength(s) => write!(
f,
"{} stream has an invalid length or alignment for its records",
s
),
Self::IoError(ref e) => write!(f, "IO error while reading PDB: {}", e),
Self::UnimplementedFeature(feature) => {
write!(f, "Unimplemented PDB feature: {}", feature)
}
Self::UnimplementedSymbolKind(kind) => write!(
f,
"Support for symbols of kind {:#06x} is not implemented",
kind
),
Self::InvalidTypeInformationHeader(reason) => {
write!(f, "The type information header was invalid: {}", reason)
}
Self::TypeNotFound(type_index) => write!(f, "Type {} not found", type_index),
Self::TypeNotIndexed(type_index, indexed_count) => write!(
f,
"Type {} not indexed (index covers {})",
type_index, indexed_count
),
Self::UnimplementedTypeKind(kind) => write!(
f,
"Support for types of kind {:#06x} is not implemented",
kind
),
Self::NotACrossModuleRef(index) => {
write!(f, "Type {:#06x} is not a cross module reference", index)
}
Self::CrossModuleRefNotFound(index) => write!(
f,
"Cross module reference {:#06x} not found in imports",
index
),
Self::UnexpectedNumericPrefix(prefix) => write!(
f,
"Variable-length numeric parsing encountered an unexpected prefix ({:#06x}",
prefix
),
Self::UnimplementedDebugSubsection(kind) => write!(
f,
"Debug module subsection of kind {:#06x} is not implemented",
kind
),
Self::UnimplementedFileChecksumKind(kind) => {
write!(f, "Unknown source file checksum kind {}", kind)
}
Self::InvalidFileChecksumOffset(offset) => {
write!(f, "Invalid source file checksum offset {:#x}", offset)
}
Self::UnknownBinaryAnnotation(num) => write!(f, "Unknown binary annotation {}", num),
_ => fmt::Debug::fmt(self, f),
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Self::IoError(e)
}
}
impl From<scroll::Error> for Error {
fn from(e: scroll::Error) -> Self {
match e {
scroll::Error::BadOffset(_) | scroll::Error::TooBig { .. } => Self::UnexpectedEof,
_ => Self::ScrollError(e),
}
}
}
pub type Result<T> = result::Result<T, Error>;
macro_rules! impl_pread {
($type:ty) => {
impl<'a> TryFromCtx<'a, Endian> for $type {
type Error = scroll::Error;
fn try_from_ctx(this: &'a [u8], le: Endian) -> scroll::Result<(Self, usize)> {
TryFromCtx::try_from_ctx(this, le).map(|(i, s)| (Self(i), s))
}
}
};
}
macro_rules! impl_hex_fmt {
($type:ty) => {
impl fmt::Display for $type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl fmt::Debug for $type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($type), "({})"), self)
}
}
};
}
macro_rules! impl_convert {
($type:ty, $inner:ty) => {
impl From<$inner> for $type {
fn from(offset: $inner) -> Self {
Self(offset)
}
}
impl From<$type> for $inner {
fn from(string_ref: $type) -> Self {
string_ref.0
}
}
};
}
macro_rules! impl_opt {
($type:ty, $none:literal) => {
impl $type {
#[inline]
pub const fn none() -> Self {
Self($none)
}
#[inline]
#[must_use]
pub fn is_some(self) -> bool {
self.0 != $none
}
#[inline]
#[must_use]
pub fn is_none(self) -> bool {
self.0 == $none
}
}
impl Default for $type {
#[inline]
fn default() -> Self {
Self::none()
}
}
};
}
macro_rules! impl_va {
($type:ty) => {
impl $type {
pub fn checked_add(self, offset: u32) -> Option<Self> {
Some(Self(self.0.checked_add(offset)?))
}
pub fn checked_sub(self, other: Self) -> Option<u32> {
self.0.checked_sub(other.0)
}
pub fn saturating_add(self, offset: u32) -> Self {
Self(self.0.saturating_add(offset))
}
pub fn saturating_sub(self, other: Self) -> u32 {
self.0.saturating_sub(other.0)
}
pub fn wrapping_add(self, offset: u32) -> Self {
Self(self.0.wrapping_add(offset))
}
pub fn wrapping_sub(self, other: Self) -> u32 {
self.0.wrapping_sub(other.0)
}
}
impl Add<u32> for $type {
type Output = Self;
#[inline]
fn add(mut self, offset: u32) -> Self {
self.0 += offset;
self
}
}
impl AddAssign<u32> for $type {
#[inline]
fn add_assign(&mut self, offset: u32) {
self.0 += offset;
}
}
impl Sub for $type {
type Output = u32;
fn sub(self, other: Self) -> Self::Output {
self.0 - other.0
}
}
impl_convert!($type, u32);
impl_hex_fmt!($type);
};
}
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Rva(pub u32);
impl_va!(Rva);
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct PdbInternalRva(pub u32);
impl_va!(PdbInternalRva);
impl_pread!(PdbInternalRva);
macro_rules! impl_section_offset {
($type:ty) => {
impl $type {
pub fn new(section: u16, offset: u32) -> Self {
Self { offset, section }
}
pub fn is_valid(self) -> bool {
self.section != 0
}
pub fn checked_add(mut self, offset: u32) -> Option<Self> {
self.offset = self.offset.checked_add(offset)?;
Some(self)
}
pub fn saturating_add(mut self, offset: u32) -> Self {
self.offset = self.offset.saturating_add(offset);
self
}
pub fn wrapping_add(mut self, offset: u32) -> Self {
self.offset = self.offset.wrapping_add(offset);
self
}
}
impl Add<u32> for $type {
type Output = Self;
#[inline]
fn add(mut self, offset: u32) -> Self {
self.offset += offset;
self
}
}
impl AddAssign<u32> for $type {
#[inline]
fn add_assign(&mut self, offset: u32) {
self.offset += offset;
}
}
impl PartialOrd for $type {
#[inline]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if self.section == other.section {
Some(self.offset.cmp(&other.offset))
} else {
None
}
}
}
impl fmt::Debug for $type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct(stringify!($type))
.field("section", &format_args!("{:#x}", self.section))
.field("offset", &format_args!("{:#x}", self.offset))
.finish()
}
}
};
}
#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
pub struct SectionOffset {
pub offset: u32,
pub section: u16,
}
impl_section_offset!(SectionOffset);
#[derive(Clone, Copy, Default, Eq, Hash, PartialEq)]
pub struct PdbInternalSectionOffset {
pub offset: u32,
pub section: u16,
}
impl<'t> TryFromCtx<'t, Endian> for PdbInternalSectionOffset {
type Error = scroll::Error;
fn try_from_ctx(this: &'t [u8], le: Endian) -> scroll::Result<(Self, usize)> {
let mut offset = 0;
let data = Self {
offset: this.gread_with(&mut offset, le)?,
section: this.gread_with(&mut offset, le)?,
};
Ok((data, offset))
}
}
impl_section_offset!(PdbInternalSectionOffset);
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct StreamIndex(pub u16);
impl StreamIndex {
#[inline]
pub(crate) fn msf_number(self) -> Option<u32> {
match self.0 {
0xffff => None,
index => Some(u32::from(index)),
}
}
}
impl fmt::Display for StreamIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.msf_number() {
Some(number) => write!(f, "{}", number),
None => write!(f, "None"),
}
}
}
impl fmt::Debug for StreamIndex {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "StreamIndex({})", self)
}
}
impl_opt!(StreamIndex, 0xffff);
impl_pread!(StreamIndex);
pub trait ItemIndex:
Copy + Default + fmt::Debug + fmt::Display + PartialEq + PartialOrd + From<u32> + Into<u32>
{
fn is_cross_module(self) -> bool {
(self.into() & 0x8000_0000) != 0
}
}
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct TypeIndex(pub u32);
impl_convert!(TypeIndex, u32);
impl_hex_fmt!(TypeIndex);
impl_pread!(TypeIndex);
impl ItemIndex for TypeIndex {}
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct IdIndex(pub u32);
impl_convert!(IdIndex, u32);
impl_hex_fmt!(IdIndex);
impl_pread!(IdIndex);
impl ItemIndex for IdIndex {}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Local<I: ItemIndex>(pub I);
impl<I> fmt::Display for Local<I>
where
I: ItemIndex + fmt::Display,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct StringRef(pub u32);
impl_convert!(StringRef, u32);
impl_hex_fmt!(StringRef);
impl_pread!(StringRef);
#[derive(Clone, Copy, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct FileIndex(pub u32);
impl_convert!(FileIndex, u32);
impl_hex_fmt!(FileIndex);
impl_pread!(FileIndex);
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct SymbolIndex(pub u32);
impl_convert!(SymbolIndex, u32);
impl_hex_fmt!(SymbolIndex);
impl_pread!(SymbolIndex);
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct Register(pub u16);
impl_convert!(Register, u16);
impl_pread!(Register);
#[derive(Debug, Default, Clone)]
pub(crate) struct ParseBuffer<'b>(&'b [u8], usize);
macro_rules! def_parse {
( $( ($n:ident, $t:ty) ),* $(,)* ) => {
$(#[doc(hidden)]
#[inline]
#[allow(unused)]
pub fn $n(&mut self) -> Result<$t> {
self.parse()
})*
}
}
macro_rules! def_peek {
( $( ($n:ident, $t:ty) ),* $(,)* ) => {
$(#[doc(hidden)]
#[inline]
pub fn $n(&mut self) -> Result<$t> {
Ok(self.0.pread_with(self.1, LE)?)
})*
}
}
impl<'b> ParseBuffer<'b> {
#[inline]
pub fn len(&self) -> usize {
self.0.len() - self.1
}
#[inline]
pub fn is_empty(&self) -> bool {
self.len() == 0
}
#[inline]
pub fn pos(&self) -> usize {
self.1
}
#[inline]
pub fn seek(&mut self, pos: usize) {
self.1 = std::cmp::min(pos, self.0.len());
}
#[inline]
pub fn truncate(&mut self, len: usize) -> Result<()> {
if self.0.len() >= len {
self.0 = &self.0[..len];
Ok(())
} else {
Err(Error::UnexpectedEof)
}
}
#[inline]
pub fn align(&mut self, alignment: usize) -> Result<()> {
let diff = self.1 % alignment;
if diff > 0 {
if self.len() < (alignment - diff) {
return Err(Error::UnexpectedEof);
}
self.1 += alignment - diff;
}
Ok(())
}
pub fn parse<T>(&mut self) -> Result<T>
where
T: TryFromCtx<'b, Endian, [u8]>,
T::Error: From<scroll::Error>,
Error: From<T::Error>,
{
Ok(self.0.gread_with(&mut self.1, LE)?)
}
pub fn parse_with<T, C>(&mut self, ctx: C) -> Result<T>
where
T: TryFromCtx<'b, C, [u8]>,
T::Error: From<scroll::Error>,
Error: From<T::Error>,
C: Copy,
{
Ok(self.0.gread_with(&mut self.1, ctx)?)
}
def_parse!(
(parse_u8, u8),
(parse_u16, u16),
(parse_i16, i16),
(parse_u32, u32),
(parse_i32, i32),
(parse_u64, u64),
(parse_i64, i64),
);
def_peek!((peek_u8, u8), (peek_u16, u16),);
#[inline]
pub fn parse_cstring(&mut self) -> Result<RawString<'b>> {
let input = &self.0[self.1..];
let null_idx = input.iter().position(|ch| *ch == 0);
if let Some(idx) = null_idx {
self.1 += idx + 1;
Ok(RawString::from(&input[..idx]))
} else {
Err(Error::UnexpectedEof)
}
}
#[inline]
pub fn parse_u8_pascal_string(&mut self) -> Result<RawString<'b>> {
let length = self.parse_u8()? as usize;
Ok(RawString::from(self.take(length)?))
}
#[inline]
pub fn take(&mut self, n: usize) -> Result<&'b [u8]> {
let input = &self.0[self.1..];
if input.len() >= n {
self.1 += n;
Ok(&input[..n])
} else {
Err(Error::UnexpectedEof)
}
}
}
impl<'b> From<&'b [u8]> for ParseBuffer<'b> {
fn from(buf: &'b [u8]) -> Self {
ParseBuffer(buf, 0)
}
}
impl<'b> fmt::LowerHex for ParseBuffer<'b> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> result::Result<(), fmt::Error> {
write!(f, "ParseBuf::from(\"")?;
for byte in self.0 {
write!(f, "\\x{:02x}", byte)?;
}
write!(f, "\").as_bytes() at offset {}", self.1)
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[allow(missing_docs)]
pub enum Variant {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
}
impl fmt::Display for Variant {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::U8(value) => write!(f, "{}", value),
Self::U16(value) => write!(f, "{}", value),
Self::U32(value) => write!(f, "{}", value),
Self::U64(value) => write!(f, "{}", value),
Self::I8(value) => write!(f, "{}", value),
Self::I16(value) => write!(f, "{}", value),
Self::I32(value) => write!(f, "{}", value),
Self::I64(value) => write!(f, "{}", value),
}
}
}
impl<'a> TryFromCtx<'a, Endian> for Variant {
type Error = Error;
fn try_from_ctx(this: &'a [u8], le: Endian) -> Result<(Self, usize)> {
let mut offset = 0;
let variant = match this.gread_with(&mut offset, le)? {
value if value < constants::LF_NUMERIC => Self::U16(value),
constants::LF_CHAR => Self::U8(this.gread_with(&mut offset, le)?),
constants::LF_SHORT => Self::I16(this.gread_with(&mut offset, le)?),
constants::LF_LONG => Self::I32(this.gread_with(&mut offset, le)?),
constants::LF_QUADWORD => Self::I64(this.gread_with(&mut offset, le)?),
constants::LF_USHORT => Self::U16(this.gread_with(&mut offset, le)?),
constants::LF_ULONG => Self::U32(this.gread_with(&mut offset, le)?),
constants::LF_UQUADWORD => Self::U64(this.gread_with(&mut offset, le)?),
_ if cfg!(debug_assertions) => unreachable!(),
other => return Err(Error::UnexpectedNumericPrefix(other)),
};
Ok((variant, offset))
}
}
#[derive(Clone, Copy, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub struct RawString<'b>(&'b [u8]);
impl fmt::Debug for RawString<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RawString({:?})", self.to_string())
}
}
impl fmt::Display for RawString<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.to_string())
}
}
impl<'b> RawString<'b> {
#[inline]
pub fn as_bytes(&self) -> &'b [u8] {
self.0
}
#[inline]
pub fn len(&self) -> usize {
self.0.len()
}
#[inline]
pub fn is_empty(&self) -> bool {
self.0.len() == 0
}
#[inline]
pub fn to_string(&self) -> Cow<'b, str> {
String::from_utf8_lossy(self.0)
}
}
impl<'b> From<RawString<'b>> for &'b [u8] {
fn from(str: RawString<'b>) -> Self {
str.as_bytes()
}
}
impl<'b> From<&'b str> for RawString<'b> {
fn from(buf: &'b str) -> Self {
RawString(buf.as_bytes())
}
}
impl<'b> From<&'b [u8]> for RawString<'b> {
fn from(buf: &'b [u8]) -> Self {
RawString(buf)
}
}
pub(crate) fn cast_aligned<T>(data: &[u8]) -> Option<&[T]> {
let alignment = mem::align_of::<T>();
let size = mem::size_of::<T>();
let ptr = data.as_ptr();
let bytes = data.len();
match (bytes % size, ptr.align_offset(alignment)) {
(0, 0) => Some(unsafe { slice::from_raw_parts(ptr as *const T, bytes / size) }),
(_, _) => None,
}
}
#[cfg(test)]
mod tests {
mod parse_buffer {
use crate::common::*;
#[test]
fn test_parse_u8() {
let vec: Vec<u8> = vec![1, 2, 3, 4];
let mut buf = ParseBuffer::from(vec.as_slice());
assert_eq!(buf.pos(), 0);
assert_eq!(buf.peek_u8().expect("peek"), 1);
assert_eq!(buf.peek_u8().expect("peek"), 1);
assert_eq!(buf.peek_u8().expect("peek"), 1);
let val = buf.parse_u8().unwrap();
assert_eq!(buf.len(), 3);
assert_eq!(buf.pos(), 1);
assert_eq!(val, 1);
assert_eq!(buf.peek_u8().expect("peek"), 2);
let val = buf.parse_u8().unwrap();
assert_eq!(buf.len(), 2);
assert_eq!(buf.pos(), 2);
assert_eq!(val, 2);
assert_eq!(buf.peek_u8().expect("peek"), 3);
let val = buf.parse_u8().unwrap();
assert_eq!(buf.len(), 1);
assert_eq!(buf.pos(), 3);
assert_eq!(val, 3);
assert_eq!(buf.peek_u8().expect("peek"), 4);
let val = buf.parse_u8().unwrap();
assert_eq!(buf.len(), 0);
assert_eq!(buf.pos(), 4);
assert_eq!(val, 4);
match buf.parse_u8() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
}
#[test]
fn test_parse_u16() {
let vec: Vec<u8> = vec![1, 2, 3];
let mut buf = ParseBuffer::from(vec.as_slice());
assert_eq!(buf.peek_u16().expect("peek"), 0x0201);
assert_eq!(buf.peek_u16().expect("peek"), 0x0201);
let val = buf.parse_u16().unwrap();
assert_eq!(buf.len(), 1);
assert_eq!(buf.pos(), 2);
assert_eq!(val, 0x0201);
match buf.parse_u16() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
buf.take(1).unwrap();
match buf.parse_u16() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
}
#[test]
fn test_parse_u32() {
let vec: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7];
let mut buf = ParseBuffer::from(vec.as_slice());
let val = buf.parse_u32().unwrap();
assert_eq!(buf.len(), 3);
assert_eq!(buf.pos(), 4);
assert_eq!(val, 0x0403_0201);
match buf.parse_u32() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
buf.take(1).unwrap();
assert_eq!(buf.pos(), 5);
match buf.parse_u32() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
buf.take(1).unwrap();
assert_eq!(buf.pos(), 6);
match buf.parse_u32() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
buf.take(1).unwrap();
assert_eq!(buf.pos(), 7);
match buf.parse_u32() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
}
#[test]
fn test_parse_u64() {
let vec: Vec<u8> = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];
let mut buf = ParseBuffer::from(vec.as_slice());
let val = buf.parse_u64().unwrap();
assert_eq!(val, 0x0807_0605_0403_0201);
match buf.parse_u64() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
}
#[test]
fn test_parse_i32() {
let vec: Vec<u8> = vec![254, 255, 255, 255, 5, 6, 7];
let mut buf = ParseBuffer::from(vec.as_slice());
let val = buf.parse_i32().unwrap();
assert_eq!(buf.len(), 3);
assert_eq!(val, -2);
assert_eq!(buf.pos(), 4);
match buf.parse_u32() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
buf.take(1).unwrap();
match buf.parse_u32() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
buf.take(1).unwrap();
match buf.parse_u32() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
buf.take(1).unwrap();
match buf.parse_u32() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
}
#[test]
fn test_parse_cstring() {
let mut buf = ParseBuffer::from(&b"hello\x00world\x00\x00\x01"[..]);
let val = buf.parse_cstring().unwrap();
assert_eq!(buf.len(), 8);
assert_eq!(buf.pos(), 6);
assert_eq!(val, RawString::from(&b"hello"[..]));
let val = buf.parse_cstring().unwrap();
assert_eq!(buf.len(), 2);
assert_eq!(buf.pos(), 12);
assert_eq!(val, RawString::from(&b"world"[..]));
let val = buf.parse_cstring().unwrap();
assert_eq!(buf.len(), 1);
assert_eq!(buf.pos(), 13);
assert_eq!(val, RawString::from(&b""[..]));
match buf.parse_cstring() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
}
#[test]
fn test_parse_u8_pascal_string() {
let mut buf = ParseBuffer::from(&b"\x05hello\x05world\x00\x01"[..]);
let val = buf.parse_u8_pascal_string().unwrap();
assert_eq!(buf.len(), 8);
assert_eq!(buf.pos(), 6);
assert_eq!(val, RawString::from(&b"hello"[..]));
let val = buf.parse_u8_pascal_string().unwrap();
assert_eq!(buf.len(), 2);
assert_eq!(buf.pos(), 12);
assert_eq!(val, RawString::from(&b"world"[..]));
let val = buf.parse_u8_pascal_string().unwrap();
assert_eq!(buf.len(), 1);
assert_eq!(buf.pos(), 13);
assert_eq!(val, RawString::from(&b""[..]));
match buf.parse_u8_pascal_string() {
Err(Error::UnexpectedEof) => (),
_ => panic!("expected EOF"),
}
}
#[test]
fn test_parse_buffer_align() {
let mut buf = ParseBuffer::from(&b"1234"[..]);
buf.take(1).unwrap();
assert!(buf.align(4).is_ok());
assert_eq!(buf.pos(), 4);
assert_eq!(buf.len(), 0);
let mut buf = ParseBuffer::from(&b"1234"[..]);
buf.take(3).unwrap();
assert!(buf.align(4).is_ok());
assert_eq!(buf.pos(), 4);
assert_eq!(buf.len(), 0);
let mut buf = ParseBuffer::from(&b"12345"[..]);
buf.take(3).unwrap();
assert!(buf.align(4).is_ok());
assert_eq!(buf.pos(), 4);
assert_eq!(buf.len(), 1);
let mut buf = ParseBuffer::from(&b"123"[..]);
buf.take(3).unwrap();
assert!(buf.align(4).is_err());
}
#[test]
fn test_seek() {
let mut buf = ParseBuffer::from(&b"hello"[..]);
buf.seek(5);
assert_eq!(buf.pos(), 5);
buf.seek(2);
assert_eq!(buf.pos(), 2);
buf.seek(10);
assert_eq!(buf.pos(), 5);
}
}
mod newtypes {
use crate::common::*;
#[test]
fn test_format_newtype() {
let val = SymbolIndex(0x42);
assert_eq!(format!("{}", val), "0x42");
}
#[test]
fn test_debug_newtype() {
let val = SymbolIndex(0x42);
assert_eq!(format!("{:?}", val), "SymbolIndex(0x42)");
}
#[test]
fn test_pread() {
let mut buf = ParseBuffer::from(&[0x42, 0, 0, 0][..]);
let val = buf.parse::<SymbolIndex>().expect("parse");
assert_eq!(val, SymbolIndex(0x42));
assert!(buf.is_empty());
}
}
mod cast_aligned {
use crate::common::cast_aligned;
use std::slice;
#[test]
fn test_cast_aligned() {
let data: &[u32] = &[1, 2, 3];
let ptr = data.as_ptr() as *const u8;
let bin: &[u8] = unsafe { slice::from_raw_parts(ptr, 12) };
assert_eq!(cast_aligned(bin), Some(data));
}
#[test]
fn test_cast_empty() {
let data: &[u32] = &[];
let ptr = data.as_ptr() as *const u8;
let bin: &[u8] = unsafe { slice::from_raw_parts(ptr, 0) };
assert_eq!(cast_aligned(bin), Some(data));
}
#[test]
fn test_cast_unaligned() {
let data: &[u32] = &[1, 2, 3];
let ptr = data.as_ptr() as *const u8;
let bin: &[u8] = unsafe { slice::from_raw_parts(ptr.offset(2), 8) };
assert_eq!(cast_aligned::<u32>(bin), None);
}
#[test]
fn test_cast_wrong_size() {
let data: &[u32] = &[1, 2, 3];
let ptr = data.as_ptr() as *const u8;
let bin: &[u8] = unsafe { slice::from_raw_parts(ptr, 11) };
assert_eq!(cast_aligned::<u32>(bin), None);
}
}
}