symbolic_ppdb/cache/
mod.rs1pub(crate) mod lookup;
51pub(crate) mod raw;
52pub(crate) mod writer;
53
54use symbolic_common::{AsSelf, DebugId};
55use thiserror::Error;
56use watto::{align_to, Pod};
57
58const PPDBCACHE_VERSION: u32 = 1;
59
60#[derive(Debug, Clone, Copy, Error)]
62#[non_exhaustive]
63pub enum CacheErrorKind {
64 #[error("could not read header")]
66 InvalidHeader,
67 #[error("wrong endianness")]
69 WrongEndianness,
70 #[error("invalid magic: {0}")]
72 InvalidMagic(u32),
73 #[error("wrong version: {0}")]
75 WrongVersion(u32),
76 #[error("could not read ranges")]
78 InvalidRanges,
79 #[error("could not read source locations")]
81 InvalidSourceLocations,
82 #[error("could not read files")]
84 InvalidFiles,
85 #[error("expected {expected} string bytes, found {found}")]
87 UnexpectedStringBytes {
88 expected: usize,
90 found: usize,
92 },
93 #[error("error processing portable pdb file")]
95 PortablePdb,
96}
97
98#[derive(Debug, Error)]
100#[error("{kind}")]
101pub struct CacheError {
102 pub(crate) kind: CacheErrorKind,
103 #[source]
104 pub(crate) source: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
105}
106
107impl CacheError {
108 pub(crate) fn new<E>(kind: CacheErrorKind, source: E) -> Self
111 where
112 E: Into<Box<dyn std::error::Error + Send + Sync>>,
113 {
114 let source = Some(source.into());
115 Self { kind, source }
116 }
117
118 pub fn kind(&self) -> CacheErrorKind {
120 self.kind
121 }
122}
123
124impl From<CacheErrorKind> for CacheError {
125 fn from(kind: CacheErrorKind) -> Self {
126 Self { kind, source: None }
127 }
128}
129
130impl From<crate::format::FormatError> for CacheError {
131 fn from(e: crate::format::FormatError) -> Self {
132 Self::new(CacheErrorKind::PortablePdb, e)
133 }
134}
135
136#[derive(Clone, PartialEq, Eq)]
141pub struct PortablePdbCache<'data> {
142 header: &'data raw::Header,
143 files: &'data [raw::File],
144 source_locations: &'data [raw::SourceLocation],
145 ranges: &'data [raw::Range],
146 string_bytes: &'data [u8],
147}
148
149impl<'data> PortablePdbCache<'data> {
150 pub fn parse(buf: &'data [u8]) -> Result<Self, CacheError> {
152 let (header, rest) =
153 raw::Header::ref_from_prefix(buf).ok_or(CacheErrorKind::InvalidHeader)?;
154
155 if header.magic == raw::PPDBCACHE_MAGIC_FLIPPED {
156 return Err(CacheErrorKind::WrongEndianness.into());
157 }
158 if header.magic != raw::PPDBCACHE_MAGIC {
159 return Err(CacheErrorKind::InvalidMagic(header.magic).into());
160 }
161
162 if header.version != PPDBCACHE_VERSION {
163 return Err(CacheErrorKind::WrongVersion(header.version).into());
164 }
165
166 let (_, rest) = align_to(rest, 8).ok_or(CacheErrorKind::InvalidFiles)?;
167
168 let (files, rest) = raw::File::slice_from_prefix(rest, header.num_files as usize)
169 .ok_or(CacheErrorKind::InvalidFiles)?;
170
171 let (_, rest) = align_to(rest, 8).ok_or(CacheErrorKind::InvalidSourceLocations)?;
172
173 let (source_locations, rest) =
174 raw::SourceLocation::slice_from_prefix(rest, header.num_ranges as usize)
175 .ok_or(CacheErrorKind::InvalidSourceLocations)?;
176
177 let (_, rest) = align_to(rest, 8).ok_or(CacheErrorKind::InvalidRanges)?;
178
179 let (ranges, rest) = raw::Range::slice_from_prefix(rest, header.num_ranges as usize)
180 .ok_or(CacheErrorKind::InvalidRanges)?;
181
182 let (_, rest) = align_to(rest, 8).ok_or(CacheErrorKind::UnexpectedStringBytes {
183 expected: header.string_bytes as usize,
184 found: 0,
185 })?;
186
187 if rest.len() < header.string_bytes as usize {
188 return Err(CacheErrorKind::UnexpectedStringBytes {
189 expected: header.string_bytes as usize,
190 found: rest.len(),
191 }
192 .into());
193 }
194
195 Ok(Self {
196 header,
197 files,
198 source_locations,
199 ranges,
200 string_bytes: rest,
201 })
202 }
203
204 pub fn debug_id(&self) -> DebugId {
206 self.header.pdb_id
207 }
208}
209
210impl std::fmt::Debug for PortablePdbCache<'_> {
211 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
212 f.debug_struct("PortablePdbCache")
213 .field("version", &self.header.version)
214 .field("debug_id", &self.debug_id())
215 .field("files", &self.header.num_files)
216 .field("ranges", &self.header.num_ranges)
217 .field("string_bytes", &self.header.string_bytes)
218 .finish()
219 }
220}
221
222impl<'slf, 'd: 'slf> AsSelf<'slf> for PortablePdbCache<'d> {
223 type Ref = PortablePdbCache<'slf>;
224
225 fn as_self(&'slf self) -> &'slf Self::Ref {
226 self
227 }
228}