use std::borrow::Cow;
use std::fmt;
use std::io;
use std::ops::{Add, Sub};
use std::result;
use scroll::ctx::TryFromCtx;
use scroll::{self, Endian, Pread, LE};
use crate::tpi::constants;
pub type TypeIndex = u32;
#[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(TypeIndex),
TypeNotIndexed(TypeIndex, TypeIndex),
UnimplementedTypeKind(u16),
UnexpectedNumericPrefix(u16),
AddressMapNotFound,
ScrollError(scroll::Error),
UnimplementedDebugSubsection(u32),
UnimplementedFileChecksumKind(u8),
InvalidFileChecksumOffset(u32),
LinesNotFound,
}
impl std::error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::UnrecognizedFileFormat => {
"The input data was not recognized as a MSF (PDB) file"
}
Error::InvalidPageSize(_) => "The MSF header specifies an invalid page size",
Error::PageReferenceOutOfRange(_) => "MSF referred to page number out of range",
Error::StreamNotFound(_) => "The requested stream is not stored in this file",
Error::StreamNameNotFound => "The requested stream is not stored in this file",
Error::InvalidStreamLength(_) => "Stream has an invalid length",
Error::IoError(ref e) => e.description(),
Error::UnexpectedEof => "Unexpectedly reached end of input",
Error::UnimplementedFeature(_) => "Unimplemented PDB feature",
Error::GlobalSymbolsNotFound => "The global symbol stream is missing",
Error::SymbolTooShort => "A symbol record's length value was impossibly small",
Error::UnimplementedSymbolKind(_) => {
"Support for symbols of this kind is not implemented"
}
Error::InvalidTypeInformationHeader(_) => "The type information header was invalid",
Error::TypeTooShort => "A type record's length value was impossibly small",
Error::TypeNotFound(_) => "Type not found",
Error::TypeNotIndexed(_, _) => "Type not indexed",
Error::UnimplementedTypeKind(_) => "Support for types of this kind is not implemented",
Error::UnexpectedNumericPrefix(_) => {
"Variable-length numeric parsing encountered an unexpected prefix"
}
Error::AddressMapNotFound => {
"Required mapping for virtual addresses (OMAP) was not found"
}
Error::ScrollError(ref e) => e.description(),
Error::UnimplementedDebugSubsection(_) => {
"Debug module subsection of this kind is not implemented"
}
Error::UnimplementedFileChecksumKind(_) => "Unknown source file checksum kind",
Error::InvalidFileChecksumOffset(_) => "Invalid source file checksum offset",
Error::LinesNotFound => "Line information not found for a module",
}
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> ::std::result::Result<(), fmt::Error> {
match *self {
Error::PageReferenceOutOfRange(p) => {
write!(f, "MSF referred to page number ({}) out of range", p)
}
Error::InvalidPageSize(n) => write!(
f,
"The MSF header specifies an invalid page size ({} bytes)",
n
),
Error::StreamNotFound(s) => {
write!(f, "The requested stream ({}) is not stored in this file", s)
}
Error::InvalidStreamLength(s) => write!(
f,
"{} stream has a length that is not a multiple of its records",
s
),
Error::IoError(ref e) => write!(f, "IO error while reading PDB: {}", e),
Error::UnimplementedFeature(feature) => {
write!(f, "Unimplemented PDB feature: {}", feature)
}
Error::UnimplementedSymbolKind(kind) => write!(
f,
"Support for symbols of kind 0x{:04x} is not implemented",
kind
),
Error::InvalidTypeInformationHeader(reason) => {
write!(f, "The type information header was invalid: {}", reason)
}
Error::TypeNotFound(type_index) => write!(f, "Type {} not found", type_index),
Error::TypeNotIndexed(type_index, indexed_count) => write!(
f,
"Type {} not indexed (index covers {})",
type_index, indexed_count
),
Error::UnimplementedTypeKind(kind) => write!(
f,
"Support for types of kind 0x{:04x} is not implemented",
kind
),
Error::UnexpectedNumericPrefix(prefix) => write!(
f,
"Variable-length numeric parsing encountered an unexpected prefix (0x{:04x}",
prefix
),
Error::UnimplementedDebugSubsection(kind) => write!(
f,
"Debug module subsection of kind 0x{:04x} is not implemented",
kind
),
Error::UnimplementedFileChecksumKind(kind) => {
write!(f, "Unknown source file checksum kind {}", kind)
}
Error::InvalidFileChecksumOffset(offset) => {
write!(f, "Invalid source file checksum offset {:#x}", offset)
}
_ => fmt::Debug::fmt(self, f),
}
}
}
impl From<io::Error> for Error {
fn from(e: io::Error) -> Self {
Error::IoError(e)
}
}
impl From<scroll::Error> for Error {
fn from(e: scroll::Error) -> Self {
match e {
scroll::Error::BadOffset(_) | scroll::Error::TooBig { .. } => Error::UnexpectedEof,
_ => Error::ScrollError(e),
}
}
}
pub type Result<T> = result::Result<T, Error>;
pub(crate) struct HexFmt<T>(pub T);
impl<T> fmt::Debug for HexFmt<T>
where
T: fmt::LowerHex,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl<T> fmt::Display for HexFmt<T>
where
T: fmt::LowerHex,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
pub(crate) struct FixedHexFmt<T>(pub T);
impl<T> fmt::Debug for FixedHexFmt<T>
where
T: fmt::LowerHex,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let width = 2 * std::mem::size_of::<T>();
write!(f, "{:#01$x}", self.0, width + 2)
}
}
impl<T> fmt::Display for FixedHexFmt<T>
where
T: fmt::LowerHex,
{
#[inline]
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(self, f)
}
}
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 From<u32> for $type {
fn from(addr: u32) -> Self {
Self(addr)
}
}
impl From<$type> for u32 {
fn from(addr: $type) -> Self {
addr.0
}
}
impl Add<u32> for $type {
type Output = Self;
fn add(mut self, offset: u32) -> Self {
self.0 += offset;
self
}
}
impl Sub for $type {
type Output = u32;
fn sub(self, other: Self) -> Self::Output {
self.0 - other.0
}
}
impl fmt::Display for $type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
HexFmt(self.0).fmt(f)
}
}
impl fmt::Debug for $type {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, concat!(stringify!($type, "({})")), self)
}
}
};
}
#[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<'a> TryFromCtx<'a, Endian> for PdbInternalRva {
type Error = scroll::Error;
type Size = usize;
fn try_from_ctx(this: &'a [u8], le: Endian) -> scroll::Result<(Self, Self::Size)> {
u32::try_from_ctx(this, le).map(|(i, s)| (PdbInternalRva(i), s))
}
}
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;
fn add(mut self, offset: u32) -> Self {
self.offset += offset;
self
}
}
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", &HexFmt(self.section))
.field("offset", &FixedHexFmt(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, Pread)]
pub struct PdbInternalSectionOffset {
pub offset: u32,
pub section: u16,
}
impl_section_offset!(PdbInternalSectionOffset);
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct StreamIndex(pub u16);
impl StreamIndex {
pub fn none() -> Self {
StreamIndex(0xffff)
}
#[inline]
pub fn is_none(self) -> bool {
self.msf_number().is_none()
}
#[inline]
pub(crate) fn msf_number(self) -> Option<u32> {
match self.0 {
0xffff => None,
index => Some(u32::from(index)),
}
}
}
impl Default for StreamIndex {
fn default() -> Self {
Self::none()
}
}
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<'a> TryFromCtx<'a, Endian> for StreamIndex {
type Error = scroll::Error;
type Size = usize;
fn try_from_ctx(this: &'a [u8], le: Endian) -> scroll::Result<(Self, Self::Size)> {
u16::try_from_ctx(this, le).map(|(i, s)| (StreamIndex(i), s))
}
}
#[derive(Clone, Copy, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct StringRef(pub u32);
impl From<u32> for StringRef {
fn from(offset: u32) -> Self {
StringRef(offset)
}
}
impl From<StringRef> for u32 {
fn from(string_ref: StringRef) -> Self {
string_ref.0
}
}
impl fmt::Display for StringRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:#010x}", self.0)
}
}
impl fmt::Debug for StringRef {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "StringRef({})", self)
}
}
impl<'a> TryFromCtx<'a, Endian> for StringRef {
type Error = scroll::Error;
type Size = usize;
fn try_from_ctx(this: &'a [u8], le: Endian) -> scroll::Result<(Self, Self::Size)> {
u32::try_from_ctx(this, le).map(|(i, s)| (StringRef(i), s))
}
}
#[doc(hidden)]
#[derive(Debug, Clone)]
pub(crate) struct ParseBuffer<'b>(&'b [u8], usize);
macro_rules! def_parse {
( $( ($n:ident, $t:ty) ),* $(,)* ) => {
$(#[doc(hidden)]
#[inline]
pub fn $n(&mut self) -> Result<$t> {
Ok(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 align(&mut self, alignment: usize) -> Result<()> {
let diff = self.1 % alignment;
if diff > 0 {
if self.len() < diff {
return Err(Error::UnexpectedEof);
}
self.1 += alignment - diff;
}
Ok(())
}
pub fn parse<T>(&mut self) -> Result<T>
where
T: TryFromCtx<'b, Endian, [u8], Error = scroll::Error, Size = usize>,
{
Ok(self.0.gread_with(&mut self.1, LE)?)
}
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),);
#[doc(hidden)]
#[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)
}
}
#[doc(hidden)]
#[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)?))
}
#[doc(hidden)]
#[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)
}
}
pub fn parse_variant(&mut self) -> Result<Variant> {
let leaf = self.parse_u16()?;
if leaf < constants::LF_NUMERIC {
return Ok(Variant::U16(leaf));
}
match leaf {
constants::LF_CHAR => Ok(Variant::U8(self.parse_u8()?)),
constants::LF_SHORT => Ok(Variant::I16(self.parse_i16()?)),
constants::LF_LONG => Ok(Variant::I32(self.parse_i32()?)),
constants::LF_QUADWORD => Ok(Variant::I64(self.parse_i64()?)),
constants::LF_USHORT => Ok(Variant::U16(self.parse_u16()?)),
constants::LF_ULONG => Ok(Variant::U32(self.parse_u32()?)),
constants::LF_UQUADWORD => Ok(Variant::U64(self.parse_u64()?)),
_ => {
debug_assert!(false);
Err(Error::UnexpectedNumericPrefix(leaf))
}
}
}
#[doc(hidden)]
#[inline]
pub(crate) fn get_variant_size(&mut self) -> usize {
let leaf = self.parse_u16();
match leaf {
Ok(leaf) => {
if leaf < constants::LF_NUMERIC {
return 2;
}
match leaf {
constants::LF_CHAR => 2 + 1,
constants::LF_SHORT => 2 + 2,
constants::LF_LONG => 2 + 4,
constants::LF_QUADWORD => 2 + 8,
constants::LF_USHORT => 2 + 2,
constants::LF_ULONG => 2 + 4,
constants::LF_UQUADWORD => 2 + 8,
_ => {
debug_assert!(false);
2
}
}
}
Err(_) => {
debug_assert!(false);
2
}
}
}
}
impl Default for ParseBuffer<'_> {
fn default() -> Self {
ParseBuffer(&[], 0)
}
}
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)]
pub enum Variant {
U8(u8),
U16(u16),
U32(u32),
U64(u64),
I8(i8),
I16(i16),
I32(i32),
I64(i64),
}
impl ::std::fmt::Display for Variant {
fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
match *self {
Variant::U8(value) => write!(f, "{}", value),
Variant::U16(value) => write!(f, "{}", value),
Variant::U32(value) => write!(f, "{}", value),
Variant::U64(value) => write!(f, "{}", value),
Variant::I8(value) => write!(f, "{}", value),
Variant::I16(value) => write!(f, "{}", value),
Variant::I32(value) => write!(f, "{}", value),
Variant::I64(value) => write!(f, "{}", value),
}
}
}
#[derive(Clone, 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)
}
}
#[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("hello\x00world\x00\x00\x01".as_bytes());
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("\x05hello\x05world\x00\x01".as_bytes());
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"),
}
}
}
}