pub(crate) mod lookup;
pub(crate) mod raw;
pub(crate) mod writer;
use symbolic_common::{AsSelf, DebugId};
use thiserror::Error;
use watto::{align_to, Pod};
const PPDBCACHE_VERSION: u32 = 1;
#[derive(Debug, Clone, Copy, Error)]
#[non_exhaustive]
pub enum CacheErrorKind {
#[error("could not read header")]
InvalidHeader,
#[error("wrong endianness")]
WrongEndianness,
#[error("invalid magic: {0}")]
InvalidMagic(u32),
#[error("wrong version: {0}")]
WrongVersion(u32),
#[error("could not read ranges")]
InvalidRanges,
#[error("could not read source locations")]
InvalidSourceLocations,
#[error("could not read files")]
InvalidFiles,
#[error("expected {expected} string bytes, found {found}")]
UnexpectedStringBytes {
expected: usize,
found: usize,
},
#[error("error processing portable pdb file")]
PortablePdb,
}
#[derive(Debug, Error)]
#[error("{kind}")]
pub struct CacheError {
pub(crate) kind: CacheErrorKind,
#[source]
pub(crate) source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
}
impl CacheError {
pub(crate) fn new<E>(kind: CacheErrorKind, source: E) -> Self
where
E: Into<Box<dyn std::error::Error + Send + Sync>>,
{
let source = Some(source.into());
Self { kind, source }
}
pub fn kind(&self) -> CacheErrorKind {
self.kind
}
}
impl From<CacheErrorKind> for CacheError {
fn from(kind: CacheErrorKind) -> Self {
Self { kind, source: None }
}
}
impl From<crate::format::FormatError> for CacheError {
fn from(e: crate::format::FormatError) -> Self {
Self::new(CacheErrorKind::PortablePdb, e)
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct PortablePdbCache<'data> {
header: &'data raw::Header,
files: &'data [raw::File],
source_locations: &'data [raw::SourceLocation],
ranges: &'data [raw::Range],
string_bytes: &'data [u8],
}
impl<'data> PortablePdbCache<'data> {
pub fn parse(buf: &'data [u8]) -> Result<Self, CacheError> {
let (header, rest) =
raw::Header::ref_from_prefix(buf).ok_or(CacheErrorKind::InvalidHeader)?;
if header.magic == raw::PPDBCACHE_MAGIC_FLIPPED {
return Err(CacheErrorKind::WrongEndianness.into());
}
if header.magic != raw::PPDBCACHE_MAGIC {
return Err(CacheErrorKind::InvalidMagic(header.magic).into());
}
if header.version != PPDBCACHE_VERSION {
return Err(CacheErrorKind::WrongVersion(header.version).into());
}
let (_, rest) = align_to(rest, 8).ok_or(CacheErrorKind::InvalidFiles)?;
let (files, rest) = raw::File::slice_from_prefix(rest, header.num_files as usize)
.ok_or(CacheErrorKind::InvalidFiles)?;
let (_, rest) = align_to(rest, 8).ok_or(CacheErrorKind::InvalidSourceLocations)?;
let (source_locations, rest) =
raw::SourceLocation::slice_from_prefix(rest, header.num_ranges as usize)
.ok_or(CacheErrorKind::InvalidSourceLocations)?;
let (_, rest) = align_to(rest, 8).ok_or(CacheErrorKind::InvalidRanges)?;
let (ranges, rest) = raw::Range::slice_from_prefix(rest, header.num_ranges as usize)
.ok_or(CacheErrorKind::InvalidRanges)?;
let (_, rest) = align_to(rest, 8).ok_or(CacheErrorKind::UnexpectedStringBytes {
expected: header.string_bytes as usize,
found: 0,
})?;
if rest.len() < header.string_bytes as usize {
return Err(CacheErrorKind::UnexpectedStringBytes {
expected: header.string_bytes as usize,
found: rest.len(),
}
.into());
}
Ok(Self {
header,
files,
source_locations,
ranges,
string_bytes: rest,
})
}
pub fn debug_id(&self) -> DebugId {
self.header.pdb_id
}
}
impl std::fmt::Debug for PortablePdbCache<'_> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("PortablePdbCache")
.field("version", &self.header.version)
.field("debug_id", &self.debug_id())
.field("files", &self.header.num_files)
.field("ranges", &self.header.num_ranges)
.field("string_bytes", &self.header.string_bytes)
.finish()
}
}
impl<'slf, 'd: 'slf> AsSelf<'slf> for PortablePdbCache<'d> {
type Ref = PortablePdbCache<'slf>;
fn as_self(&'slf self) -> &'slf Self::Ref {
self
}
}