/rust/registry/src/index.crates.io-1949cf8c6b5b557f/pdb2-0.10.2/src/tpi/mod.rs
Line | Count | Source |
1 | | // Copyright 2017 pdb Developers |
2 | | // |
3 | | // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or |
4 | | // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or |
5 | | // http://opensource.org/licenses/MIT>, at your option. This file may not be |
6 | | // copied, modified, or distributed except according to those terms. |
7 | | |
8 | | use std::fmt; |
9 | | use std::marker::PhantomData; |
10 | | use std::result; |
11 | | |
12 | | use crate::common::*; |
13 | | use crate::msf::Stream; |
14 | | use crate::FallibleIterator; |
15 | | |
16 | | pub(crate) mod constants; |
17 | | mod data; |
18 | | mod header; |
19 | | mod id; |
20 | | mod primitive; |
21 | | |
22 | | use self::header::*; |
23 | | use self::primitive::type_data_for_primitive; |
24 | | |
25 | | pub use self::data::*; |
26 | | pub use self::id::*; |
27 | | pub use self::primitive::{Indirection, PrimitiveKind, PrimitiveType}; |
28 | | |
29 | | /// Zero-copy access to a PDB type or id stream. |
30 | | /// |
31 | | /// PDBs store two kinds of related streams with an identical internal structure: |
32 | | /// |
33 | | /// - [`TypeInformation`] (TPI stream) contains information on primitive types, classes and |
34 | | /// procedures, including their return type and arguments. Its contents are identified by |
35 | | /// [`TypeIndex`]. |
36 | | /// - [`IdInformation`] (IPI stream) is a stricter version of the above stream that contains inline |
37 | | /// functions, build infos and source references. Its contents are identified by [`IdIndex`]. |
38 | | /// |
39 | | /// Items in these streams are stored by their index in ascending order. Symbols declared in |
40 | | /// [`ModuleInfo`](crate::ModuleInfo) can refer to items in both streams, as well as items to other |
41 | | /// items with one exception: `Type`s cannot refer to `Id`s. Also, the PDB format requires that |
42 | | /// items refer only to types with lower indexes. Thus, the stream of items forms a directed acyclic |
43 | | /// graph. |
44 | | /// |
45 | | /// Both streams can iterate by their index using [`ItemInformation::iter`]. Additionally, |
46 | | /// [`ItemFinder`] is a secondary data structure to provide efficient backtracking for random |
47 | | /// access. |
48 | | /// |
49 | | /// There are type definitions for both streams: |
50 | | /// |
51 | | /// - `ItemInformation`: [`TypeInformation`] and [`IdInformation`] |
52 | | /// - [`ItemFinder`]: [`TypeFinder`] and [`IdFinder`] |
53 | | /// - [`ItemIndex`]: [`TypeIndex`] and [`IdIndex`] |
54 | | /// - [`ItemIter`]: [`TypeIter`] and [`IdIter`] |
55 | | /// - [`Item`]: [`Type`] and [`Id`] |
56 | | /// |
57 | | /// # Examples |
58 | | /// |
59 | | /// Iterating over the types while building a `TypeFinder`: |
60 | | /// |
61 | | /// ``` |
62 | | /// # use pdb2::FallibleIterator; |
63 | | /// # |
64 | | /// # fn test() -> pdb2::Result<usize> { |
65 | | /// # let file = std::fs::File::open("fixtures/self/foo.pdb")?; |
66 | | /// # let mut pdb = pdb2::PDB::open(file)?; |
67 | | /// |
68 | | /// let type_information = pdb.type_information()?; |
69 | | /// let mut type_finder = type_information.finder(); |
70 | | /// |
71 | | /// # let expected_count = type_information.len(); |
72 | | /// # let mut count: usize = 0; |
73 | | /// let mut iter = type_information.iter(); |
74 | | /// while let Some(typ) = iter.next()? { |
75 | | /// // build the type finder as we go |
76 | | /// type_finder.update(&iter); |
77 | | /// |
78 | | /// // parse the type record |
79 | | /// match typ.parse() { |
80 | | /// Ok(pdb2::TypeData::Class(pdb2::ClassType {name, properties, fields: Some(fields), ..})) => { |
81 | | /// // this Type describes a class-like type with fields |
82 | | /// println!("type {} is a class named {}", typ.index(), name); |
83 | | /// |
84 | | /// // `fields` is a TypeIndex which refers to a FieldList |
85 | | /// // To find information about the fields, find and parse that Type |
86 | | /// match type_finder.find(fields)?.parse()? { |
87 | | /// pdb2::TypeData::FieldList(list) => { |
88 | | /// // `fields` is a Vec<TypeData> |
89 | | /// for field in list.fields { |
90 | | /// if let pdb2::TypeData::Member(member) = field { |
91 | | /// // follow `member.field_type` as desired |
92 | | /// println!(" - field {} at offset {:x}", member.name, member.offset); |
93 | | /// } else { |
94 | | /// // handle member functions, nested types, etc. |
95 | | /// } |
96 | | /// } |
97 | | /// |
98 | | /// if let Some(more_fields) = list.continuation { |
99 | | /// // A FieldList can be split across multiple records |
100 | | /// // TODO: follow `more_fields` and handle the next FieldList |
101 | | /// } |
102 | | /// } |
103 | | /// _ => { } |
104 | | /// } |
105 | | /// |
106 | | /// }, |
107 | | /// Ok(_) => { |
108 | | /// // ignore everything that's not a class-like type |
109 | | /// }, |
110 | | /// Err(pdb2::Error::UnimplementedTypeKind(_)) => { |
111 | | /// // found an unhandled type record |
112 | | /// // this probably isn't fatal in most use cases |
113 | | /// }, |
114 | | /// Err(e) => { |
115 | | /// // other error, probably is worth failing |
116 | | /// return Err(e); |
117 | | /// } |
118 | | /// } |
119 | | /// # count += 1; |
120 | | /// } |
121 | | /// |
122 | | /// # assert_eq!(expected_count, count); |
123 | | /// # Ok(count) |
124 | | /// # } |
125 | | /// # assert!(test().expect("test") > 8000); |
126 | | /// ``` |
127 | | #[derive(Debug)] |
128 | | pub struct ItemInformation<'s, I> { |
129 | | stream: Stream<'s>, |
130 | | header: Header, |
131 | | _ph: PhantomData<&'s I>, |
132 | | } |
133 | | |
134 | | impl<'s, I> ItemInformation<'s, I> |
135 | | where |
136 | | I: ItemIndex, |
137 | | { |
138 | | /// Parses `TypeInformation` from raw stream data. |
139 | 632 | pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> { |
140 | 632 | let mut buf = stream.parse_buffer(); |
141 | 632 | let header = Header::parse(&mut buf)?; |
142 | 626 | let _ph = PhantomData; |
143 | 626 | Ok(Self { |
144 | 626 | stream, |
145 | 626 | header, |
146 | 626 | _ph, |
147 | 626 | }) |
148 | 632 | } <pdb2::tpi::ItemInformation<pdb2::common::IdIndex>>::parse Line | Count | Source | 139 | 312 | pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> { | 140 | 312 | let mut buf = stream.parse_buffer(); | 141 | 312 | let header = Header::parse(&mut buf)?; | 142 | 308 | let _ph = PhantomData; | 143 | 308 | Ok(Self { | 144 | 308 | stream, | 145 | 308 | header, | 146 | 308 | _ph, | 147 | 308 | }) | 148 | 312 | } |
<pdb2::tpi::ItemInformation<pdb2::common::TypeIndex>>::parse Line | Count | Source | 139 | 314 | pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> { | 140 | 314 | let mut buf = stream.parse_buffer(); | 141 | 314 | let header = Header::parse(&mut buf)?; | 142 | 312 | let _ph = PhantomData; | 143 | 312 | Ok(Self { | 144 | 312 | stream, | 145 | 312 | header, | 146 | 312 | _ph, | 147 | 312 | }) | 148 | 314 | } |
Unexecuted instantiation: <pdb2::tpi::ItemInformation<_>>::parse <pdb2::tpi::ItemInformation<pdb2::common::IdIndex>>::parse Line | Count | Source | 139 | 3 | pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> { | 140 | 3 | let mut buf = stream.parse_buffer(); | 141 | 3 | let header = Header::parse(&mut buf)?; | 142 | 3 | let _ph = PhantomData; | 143 | 3 | Ok(Self { | 144 | 3 | stream, | 145 | 3 | header, | 146 | 3 | _ph, | 147 | 3 | }) | 148 | 3 | } |
<pdb2::tpi::ItemInformation<pdb2::common::TypeIndex>>::parse Line | Count | Source | 139 | 3 | pub(crate) fn parse(stream: Stream<'s>) -> Result<Self> { | 140 | 3 | let mut buf = stream.parse_buffer(); | 141 | 3 | let header = Header::parse(&mut buf)?; | 142 | 3 | let _ph = PhantomData; | 143 | 3 | Ok(Self { | 144 | 3 | stream, | 145 | 3 | header, | 146 | 3 | _ph, | 147 | 3 | }) | 148 | 3 | } |
|
149 | | |
150 | | /// Returns an iterator that can traverse the type table in sequential order. |
151 | | #[must_use] |
152 | 614 | pub fn iter(&self) -> ItemIter<'_, I> { |
153 | | // get a parse buffer |
154 | 614 | let mut buf = self.stream.parse_buffer(); |
155 | | |
156 | | // drop the header |
157 | | // this can't fail; we've already read this once |
158 | 614 | buf.take(self.header.header_size as usize) |
159 | 614 | .expect("dropping TPI header"); |
160 | | |
161 | 614 | ItemIter { |
162 | 614 | buf, |
163 | 614 | index: self.header.minimum_index, |
164 | 614 | _ph: PhantomData, |
165 | 614 | } |
166 | 614 | } <pdb2::tpi::ItemInformation<pdb2::common::IdIndex>>::iter Line | Count | Source | 152 | 307 | pub fn iter(&self) -> ItemIter<'_, I> { | 153 | | // get a parse buffer | 154 | 307 | let mut buf = self.stream.parse_buffer(); | 155 | | | 156 | | // drop the header | 157 | | // this can't fail; we've already read this once | 158 | 307 | buf.take(self.header.header_size as usize) | 159 | 307 | .expect("dropping TPI header"); | 160 | | | 161 | 307 | ItemIter { | 162 | 307 | buf, | 163 | 307 | index: self.header.minimum_index, | 164 | 307 | _ph: PhantomData, | 165 | 307 | } | 166 | 307 | } |
<pdb2::tpi::ItemInformation<pdb2::common::TypeIndex>>::iter Line | Count | Source | 152 | 307 | pub fn iter(&self) -> ItemIter<'_, I> { | 153 | | // get a parse buffer | 154 | 307 | let mut buf = self.stream.parse_buffer(); | 155 | | | 156 | | // drop the header | 157 | | // this can't fail; we've already read this once | 158 | 307 | buf.take(self.header.header_size as usize) | 159 | 307 | .expect("dropping TPI header"); | 160 | | | 161 | 307 | ItemIter { | 162 | 307 | buf, | 163 | 307 | index: self.header.minimum_index, | 164 | 307 | _ph: PhantomData, | 165 | 307 | } | 166 | 307 | } |
Unexecuted instantiation: <pdb2::tpi::ItemInformation<_>>::iter |
167 | | |
168 | | /// Returns the number of items contained in this `ItemInformation`. |
169 | | /// |
170 | | /// Note that in the case of the type stream ([`TypeInformation`]) primitive types are not |
171 | | /// stored in the PDB file. The number of distinct types reachable via this table will be higher |
172 | | /// than `len()`. |
173 | | #[must_use] |
174 | 0 | pub fn len(&self) -> usize { |
175 | 0 | (self.header.maximum_index - self.header.minimum_index) as usize |
176 | 0 | } |
177 | | |
178 | | /// Returns whether this `ItemInformation` contains any data. |
179 | | #[must_use] |
180 | 0 | pub fn is_empty(&self) -> bool { |
181 | 0 | self.len() == 0 |
182 | 0 | } |
183 | | |
184 | | /// Returns an `ItemFinder` with a default time-space tradeoff useful for access by |
185 | | /// [`ItemIndex`]. |
186 | | /// |
187 | | /// The `ItemFinder` is initially empty and must be populated by iterating. See the struct-level |
188 | | /// docs for an example. |
189 | | #[must_use] |
190 | 614 | pub fn finder(&self) -> ItemFinder<'_, I> { |
191 | 614 | ItemFinder::new(self, 3) |
192 | 614 | } <pdb2::tpi::ItemInformation<pdb2::common::IdIndex>>::finder Line | Count | Source | 190 | 307 | pub fn finder(&self) -> ItemFinder<'_, I> { | 191 | 307 | ItemFinder::new(self, 3) | 192 | 307 | } |
<pdb2::tpi::ItemInformation<pdb2::common::TypeIndex>>::finder Line | Count | Source | 190 | 307 | pub fn finder(&self) -> ItemFinder<'_, I> { | 191 | 307 | ItemFinder::new(self, 3) | 192 | 307 | } |
Unexecuted instantiation: <pdb2::tpi::ItemInformation<_>>::finder |
193 | | } |
194 | | |
195 | | /// This buffer is used when a `Type` refers to a primitive type. It doesn't contain anything |
196 | | /// type-specific, but it does parse as `raw_type() == 0xffff`, which is a reserved value. Seems |
197 | | /// like a reasonable thing to do. |
198 | | const PRIMITIVE_TYPE: &[u8] = b"\xff\xff"; |
199 | | |
200 | | /// Represents an entry in the type or id stream. |
201 | | /// |
202 | | /// An `Item` has been minimally processed and may not be correctly formed or even understood by |
203 | | /// this library. To avoid copying, `Items`s exist as references to data owned by the parent |
204 | | /// `ItemInformation`. Therefore, an `Item` may not outlive its parent. |
205 | | /// |
206 | | /// The data held by items can be parsed: |
207 | | /// |
208 | | /// - [`Type::parse`](Self::parse) returns [`TypeData`]. |
209 | | /// - [`Id::parse`](Self::parse) returns [`IdData`]. |
210 | | /// |
211 | | /// Depending on the stream, this can either be a [`Type`] or [`Id`]. |
212 | | #[derive(Copy, Clone, PartialEq)] |
213 | | pub struct Item<'t, I> { |
214 | | index: I, |
215 | | data: &'t [u8], |
216 | | } |
217 | | |
218 | | impl<I> Item<'_, I> |
219 | | where |
220 | | I: ItemIndex, |
221 | | { |
222 | | /// Returns this item's index. |
223 | | /// |
224 | | /// Depending on the stream, either a [`TypeIndex`] or [`IdIndex`]. |
225 | 1.14M | pub fn index(&self) -> I { |
226 | 1.14M | self.index |
227 | 1.14M | } <pdb2::tpi::Item<pdb2::common::IdIndex>>::index Line | Count | Source | 225 | 272k | pub fn index(&self) -> I { | 226 | 272k | self.index | 227 | 272k | } |
<pdb2::tpi::Item<pdb2::common::TypeIndex>>::index Line | Count | Source | 225 | 868k | pub fn index(&self) -> I { | 226 | 868k | self.index | 227 | 868k | } |
Unexecuted instantiation: <pdb2::tpi::Item<_>>::index |
228 | | |
229 | | /// Returns the the binary data length in the on-disk format. |
230 | | /// |
231 | | /// Items are prefixed by a 16-bit length number, which is not included in this length. |
232 | 0 | pub fn len(&self) -> usize { |
233 | 0 | self.data.len() |
234 | 0 | } |
235 | | |
236 | | /// Returns whether this items's data is empty. |
237 | | /// |
238 | | /// Items are prefixed by a 16-bit length number, which is not included in this operation. |
239 | 0 | pub fn is_empty(&self) -> bool { |
240 | 0 | self.data.is_empty() |
241 | 0 | } |
242 | | |
243 | | /// Returns the identifier of the kind of data stored by this this `Item`. |
244 | | /// |
245 | | /// As a special case, if this is a primitive [`Type`], this function will return `0xffff`. |
246 | | #[inline] |
247 | 0 | pub fn raw_kind(&self) -> u16 { |
248 | 0 | debug_assert!(self.data.len() >= 2); |
249 | | |
250 | | // assemble a little-endian u16 |
251 | 0 | u16::from(self.data[0]) | (u16::from(self.data[1]) << 8) |
252 | 0 | } |
253 | | } |
254 | | |
255 | | impl<I> fmt::Debug for Item<'_, I> |
256 | | where |
257 | | I: ItemIndex, |
258 | | { |
259 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
260 | 0 | write!( |
261 | 0 | f, |
262 | 0 | "Type{{ kind: 0x{:04x} [{} bytes] }}", |
263 | 0 | self.raw_kind(), |
264 | 0 | self.data.len() |
265 | | ) |
266 | 0 | } |
267 | | } |
268 | | |
269 | | /// In-memory index for efficient random-access to [`Item`]s by index. |
270 | | /// |
271 | | /// `ItemFinder` can be obtained via [`ItemInformation::finder`]. It starts out empty and must be |
272 | | /// populated by calling [`ItemFinder::update`] while iterating. There are two typedefs for easier |
273 | | /// use: |
274 | | /// |
275 | | /// - [`TypeFinder`] for finding [`Type`]s in a [`TypeInformation`] (TPI stream). |
276 | | /// - [`IdFinder`] for finding [`Id`]s in a [`IdInformation`] (IPI stream). |
277 | | /// |
278 | | /// `ItemFinder` allocates all the memory it needs when it is first created. The footprint is |
279 | | /// directly proportional to the total number of types; see [`ItemInformation::len`]. |
280 | | /// |
281 | | /// # Time/space trade-off |
282 | | /// |
283 | | /// The naïve approach is to store the position of each `Item` as they are covered in the stream. |
284 | | /// The cost is memory: namely one `u32` per `Item`. |
285 | | /// |
286 | | /// Compare this approach to an `ItemFinder` that stores the position of every Nth item. Memory |
287 | | /// requirements would be reduced by a factor of N in exchange for requiring an average of (N-1)/2 |
288 | | /// iterations per lookup. However, iteration is cheap sequential memory access, and spending less |
289 | | /// memory on `ItemFinder` means more of the data can fit in the cache, so this is likely a good |
290 | | /// trade-off for small-ish values of N. |
291 | | /// |
292 | | /// `ItemFinder` is parameterized by `shift` which controls this trade-off as powers of two: |
293 | | /// |
294 | | /// * If `shift` is 0, `ItemFinder` stores 4 bytes per `Item` and always performs direct lookups. |
295 | | /// * If `shift` is 1, `ItemFinder` stores 2 bytes per `Item` and averages 0.5 iterations per |
296 | | /// lookup. |
297 | | /// * If `shift` is 2, `ItemFinder` stores 1 byte per `Item` and averages 1.5 iterations per |
298 | | /// lookup. |
299 | | /// * If `shift` is 3, `ItemFinder` stores 4 bits per `Item` and averages 3.5 iterations per |
300 | | /// lookup. |
301 | | /// * If `shift` is 4, `ItemFinder` stores 2 bits per `Item` and averages 7.5 iterations per |
302 | | /// lookup. |
303 | | /// * If `shift` is 5, `ItemFinder` stores 1 bit per `Item` and averages 15.5 iterations per |
304 | | /// lookup. |
305 | | /// |
306 | | /// This list can continue but with rapidly diminishing returns. Iteration cost is proportional to |
307 | | /// item size, which varies, but typical numbers from a large program are: |
308 | | /// |
309 | | /// * 24% of items are 12 bytes |
310 | | /// * 34% of items are <= 16 bytes |
311 | | /// * 84% of items are <= 32 bytes |
312 | | /// |
313 | | /// A `shift` of 2 or 3 is likely appropriate for most workloads. 500K items would require 1 MB or |
314 | | /// 500 KB of memory respectively, and lookups -- though indirect -- would still usually need only |
315 | | /// one or two 64-byte cache lines. |
316 | | #[derive(Debug)] |
317 | | pub struct ItemFinder<'t, I> { |
318 | | buffer: ParseBuffer<'t>, |
319 | | minimum_index: u32, |
320 | | maximum_index: u32, |
321 | | positions: Vec<u32>, |
322 | | shift: u8, |
323 | | _ph: PhantomData<&'t I>, |
324 | | } |
325 | | |
326 | | impl<'t, I> ItemFinder<'t, I> |
327 | | where |
328 | | I: ItemIndex, |
329 | | { |
330 | 614 | fn new(info: &'t ItemInformation<'_, I>, shift: u8) -> Self { |
331 | | // maximum index is the highest index + 1. |
332 | 614 | let count = info.header.maximum_index - info.header.minimum_index; |
333 | | |
334 | 614 | let round_base = (1 << shift) - 1; |
335 | 614 | let shifted_count = ((count + round_base) & !round_base) >> shift; |
336 | 614 | let mut positions = Vec::with_capacity(shifted_count as usize); |
337 | | |
338 | 614 | if shifted_count > 0 { |
339 | 614 | // add record zero, which is identical regardless of shift |
340 | 614 | positions.push(info.header.header_size); |
341 | 614 | } |
342 | | |
343 | 614 | Self { |
344 | 614 | buffer: info.stream.parse_buffer(), |
345 | 614 | minimum_index: info.header.minimum_index, |
346 | 614 | maximum_index: info.header.maximum_index, |
347 | 614 | positions, |
348 | 614 | shift, |
349 | 614 | _ph: PhantomData, |
350 | 614 | } |
351 | 614 | } <pdb2::tpi::ItemFinder<pdb2::common::IdIndex>>::new Line | Count | Source | 330 | 307 | fn new(info: &'t ItemInformation<'_, I>, shift: u8) -> Self { | 331 | | // maximum index is the highest index + 1. | 332 | 307 | let count = info.header.maximum_index - info.header.minimum_index; | 333 | | | 334 | 307 | let round_base = (1 << shift) - 1; | 335 | 307 | let shifted_count = ((count + round_base) & !round_base) >> shift; | 336 | 307 | let mut positions = Vec::with_capacity(shifted_count as usize); | 337 | | | 338 | 307 | if shifted_count > 0 { | 339 | 307 | // add record zero, which is identical regardless of shift | 340 | 307 | positions.push(info.header.header_size); | 341 | 307 | } | 342 | | | 343 | 307 | Self { | 344 | 307 | buffer: info.stream.parse_buffer(), | 345 | 307 | minimum_index: info.header.minimum_index, | 346 | 307 | maximum_index: info.header.maximum_index, | 347 | 307 | positions, | 348 | 307 | shift, | 349 | 307 | _ph: PhantomData, | 350 | 307 | } | 351 | 307 | } |
<pdb2::tpi::ItemFinder<pdb2::common::TypeIndex>>::new Line | Count | Source | 330 | 307 | fn new(info: &'t ItemInformation<'_, I>, shift: u8) -> Self { | 331 | | // maximum index is the highest index + 1. | 332 | 307 | let count = info.header.maximum_index - info.header.minimum_index; | 333 | | | 334 | 307 | let round_base = (1 << shift) - 1; | 335 | 307 | let shifted_count = ((count + round_base) & !round_base) >> shift; | 336 | 307 | let mut positions = Vec::with_capacity(shifted_count as usize); | 337 | | | 338 | 307 | if shifted_count > 0 { | 339 | 307 | // add record zero, which is identical regardless of shift | 340 | 307 | positions.push(info.header.header_size); | 341 | 307 | } | 342 | | | 343 | 307 | Self { | 344 | 307 | buffer: info.stream.parse_buffer(), | 345 | 307 | minimum_index: info.header.minimum_index, | 346 | 307 | maximum_index: info.header.maximum_index, | 347 | 307 | positions, | 348 | 307 | shift, | 349 | 307 | _ph: PhantomData, | 350 | 307 | } | 351 | 307 | } |
Unexecuted instantiation: <pdb2::tpi::ItemFinder<_>>::new |
352 | | |
353 | | /// Given an index, find which position in the Vec we should jump to and how many times we |
354 | | /// need to iterate to find the requested type. |
355 | | /// |
356 | | /// `shift` refers to the size of these bit shifts. |
357 | | #[inline] |
358 | 1.26M | fn resolve(&self, type_index: u32) -> (usize, usize) { |
359 | 1.26M | let raw = type_index - self.minimum_index; |
360 | 1.26M | ( |
361 | 1.26M | (raw >> self.shift) as usize, |
362 | 1.26M | (raw & ((1 << self.shift) - 1)) as usize, |
363 | 1.26M | ) |
364 | 1.26M | } <pdb2::tpi::ItemFinder<pdb2::common::IdIndex>>::resolve Line | Count | Source | 358 | 301k | fn resolve(&self, type_index: u32) -> (usize, usize) { | 359 | 301k | let raw = type_index - self.minimum_index; | 360 | 301k | ( | 361 | 301k | (raw >> self.shift) as usize, | 362 | 301k | (raw & ((1 << self.shift) - 1)) as usize, | 363 | 301k | ) | 364 | 301k | } |
<pdb2::tpi::ItemFinder<pdb2::common::TypeIndex>>::resolve Line | Count | Source | 358 | 962k | fn resolve(&self, type_index: u32) -> (usize, usize) { | 359 | 962k | let raw = type_index - self.minimum_index; | 360 | 962k | ( | 361 | 962k | (raw >> self.shift) as usize, | 362 | 962k | (raw & ((1 << self.shift) - 1)) as usize, | 363 | 962k | ) | 364 | 962k | } |
Unexecuted instantiation: <pdb2::tpi::ItemFinder<_>>::resolve |
365 | | |
366 | | /// Returns the highest index which is currently served by this `ItemFinder`. |
367 | | /// |
368 | | /// When iterating through the stream, you shouldn't need to consider this. Items only ever |
369 | | /// reference lower indexes. However, when loading items referenced by the symbols stream, this |
370 | | /// can be useful to check whether iteration is required. |
371 | | #[inline] |
372 | | #[must_use] |
373 | 180k | pub fn max_index(&self) -> I { |
374 | 180k | I::from(match self.positions.len() { |
375 | 0 | 0 => 0, // special case for an empty type index |
376 | 180k | len => (len << self.shift) as u32 + self.minimum_index - 1, |
377 | | }) |
378 | 180k | } <pdb2::tpi::ItemFinder<pdb2::common::IdIndex>>::max_index Line | Count | Source | 373 | 40.9k | pub fn max_index(&self) -> I { | 374 | 40.9k | I::from(match self.positions.len() { | 375 | 0 | 0 => 0, // special case for an empty type index | 376 | 40.9k | len => (len << self.shift) as u32 + self.minimum_index - 1, | 377 | | }) | 378 | 40.9k | } |
<pdb2::tpi::ItemFinder<pdb2::common::TypeIndex>>::max_index Line | Count | Source | 373 | 139k | pub fn max_index(&self) -> I { | 374 | 139k | I::from(match self.positions.len() { | 375 | 0 | 0 => 0, // special case for an empty type index | 376 | 139k | len => (len << self.shift) as u32 + self.minimum_index - 1, | 377 | | }) | 378 | 139k | } |
Unexecuted instantiation: <pdb2::tpi::ItemFinder<_>>::max_index |
379 | | |
380 | | /// Update this `ItemFinder` based on the current position of a [`ItemIter`]. |
381 | | /// |
382 | | /// Do this each time you call `.next()`. See documentation of [`ItemInformation`] for an |
383 | | /// example. |
384 | | #[inline] |
385 | 1.14M | pub fn update(&mut self, iterator: &ItemIter<'t, I>) { |
386 | 1.14M | let (vec_index, iteration_count) = self.resolve(iterator.index); |
387 | 1.14M | if iteration_count == 0 && vec_index == self.positions.len() { |
388 | 142k | let pos = iterator.buf.pos(); |
389 | 142k | assert!(pos < u32::MAX as usize); |
390 | 142k | self.positions.push(pos as u32); |
391 | 998k | } |
392 | 1.14M | } <pdb2::tpi::ItemFinder<pdb2::common::IdIndex>>::update Line | Count | Source | 385 | 272k | pub fn update(&mut self, iterator: &ItemIter<'t, I>) { | 386 | 272k | let (vec_index, iteration_count) = self.resolve(iterator.index); | 387 | 272k | if iteration_count == 0 && vec_index == self.positions.len() { | 388 | 33.8k | let pos = iterator.buf.pos(); | 389 | 33.8k | assert!(pos < u32::MAX as usize); | 390 | 33.8k | self.positions.push(pos as u32); | 391 | 238k | } | 392 | 272k | } |
<pdb2::tpi::ItemFinder<pdb2::common::TypeIndex>>::update Line | Count | Source | 385 | 868k | pub fn update(&mut self, iterator: &ItemIter<'t, I>) { | 386 | 868k | let (vec_index, iteration_count) = self.resolve(iterator.index); | 387 | 868k | if iteration_count == 0 && vec_index == self.positions.len() { | 388 | 108k | let pos = iterator.buf.pos(); | 389 | 108k | assert!(pos < u32::MAX as usize); | 390 | 108k | self.positions.push(pos as u32); | 391 | 759k | } | 392 | 868k | } |
Unexecuted instantiation: <pdb2::tpi::ItemFinder<_>>::update |
393 | | |
394 | | /// Find an `Item` by its index. |
395 | | /// |
396 | | /// # Errors |
397 | | /// |
398 | | /// * `Error::TypeNotFound(index)` if you ask for an item that doesn't exist. |
399 | | /// * `Error::TypeNotIndexed(index, max_index)` if you ask for an item that is known to exist |
400 | | /// but is not currently known by this `ItemFinder`. |
401 | 138k | pub fn find(&self, index: I) -> Result<Item<'t, I>> { |
402 | 138k | let index: u32 = index.into(); |
403 | 138k | if index < self.minimum_index { |
404 | 14.8k | return Ok(Item { |
405 | 14.8k | index: I::from(index), |
406 | 14.8k | data: PRIMITIVE_TYPE, |
407 | 14.8k | }); |
408 | 123k | } else if index > self.maximum_index { |
409 | 0 | return Err(Error::TypeNotFound(index)); |
410 | 123k | } |
411 | | |
412 | | // figure out where we'd find this |
413 | 123k | let (vec_index, iteration_count) = self.resolve(index); |
414 | | |
415 | 123k | if let Some(pos) = self.positions.get(vec_index) { |
416 | | // hit |
417 | 123k | let mut buf = self.buffer.clone(); |
418 | | |
419 | | // jump forwards |
420 | 123k | buf.take(*pos as usize)?; |
421 | | |
422 | | // skip some records |
423 | 123k | for _ in 0..iteration_count { |
424 | 414k | let length = buf.parse_u16()?; |
425 | 414k | buf.take(length as usize)?; |
426 | | } |
427 | | |
428 | | // read the type |
429 | 123k | let length = buf.parse_u16()?; |
430 | | |
431 | | Ok(Item { |
432 | 123k | index: I::from(index), |
433 | 123k | data: buf.take(length as usize)?, |
434 | | }) |
435 | | } else { |
436 | | // miss |
437 | 0 | Err(Error::TypeNotIndexed(index, self.max_index().into())) |
438 | | } |
439 | 138k | } <pdb2::tpi::ItemFinder<pdb2::common::IdIndex>>::find Line | Count | Source | 401 | 29.5k | pub fn find(&self, index: I) -> Result<Item<'t, I>> { | 402 | 29.5k | let index: u32 = index.into(); | 403 | 29.5k | if index < self.minimum_index { | 404 | 0 | return Ok(Item { | 405 | 0 | index: I::from(index), | 406 | 0 | data: PRIMITIVE_TYPE, | 407 | 0 | }); | 408 | 29.5k | } else if index > self.maximum_index { | 409 | 0 | return Err(Error::TypeNotFound(index)); | 410 | 29.5k | } | 411 | | | 412 | | // figure out where we'd find this | 413 | 29.5k | let (vec_index, iteration_count) = self.resolve(index); | 414 | | | 415 | 29.5k | if let Some(pos) = self.positions.get(vec_index) { | 416 | | // hit | 417 | 29.5k | let mut buf = self.buffer.clone(); | 418 | | | 419 | | // jump forwards | 420 | 29.5k | buf.take(*pos as usize)?; | 421 | | | 422 | | // skip some records | 423 | 29.5k | for _ in 0..iteration_count { | 424 | 100k | let length = buf.parse_u16()?; | 425 | 100k | buf.take(length as usize)?; | 426 | | } | 427 | | | 428 | | // read the type | 429 | 29.4k | let length = buf.parse_u16()?; | 430 | | | 431 | | Ok(Item { | 432 | 29.4k | index: I::from(index), | 433 | 29.4k | data: buf.take(length as usize)?, | 434 | | }) | 435 | | } else { | 436 | | // miss | 437 | 0 | Err(Error::TypeNotIndexed(index, self.max_index().into())) | 438 | | } | 439 | 29.5k | } |
<pdb2::tpi::ItemFinder<pdb2::common::TypeIndex>>::find Line | Count | Source | 401 | 109k | pub fn find(&self, index: I) -> Result<Item<'t, I>> { | 402 | 109k | let index: u32 = index.into(); | 403 | 109k | if index < self.minimum_index { | 404 | 14.8k | return Ok(Item { | 405 | 14.8k | index: I::from(index), | 406 | 14.8k | data: PRIMITIVE_TYPE, | 407 | 14.8k | }); | 408 | 94.4k | } else if index > self.maximum_index { | 409 | 0 | return Err(Error::TypeNotFound(index)); | 410 | 94.4k | } | 411 | | | 412 | | // figure out where we'd find this | 413 | 94.4k | let (vec_index, iteration_count) = self.resolve(index); | 414 | | | 415 | 94.4k | if let Some(pos) = self.positions.get(vec_index) { | 416 | | // hit | 417 | 94.4k | let mut buf = self.buffer.clone(); | 418 | | | 419 | | // jump forwards | 420 | 94.4k | buf.take(*pos as usize)?; | 421 | | | 422 | | // skip some records | 423 | 94.4k | for _ in 0..iteration_count { | 424 | 313k | let length = buf.parse_u16()?; | 425 | 313k | buf.take(length as usize)?; | 426 | | } | 427 | | | 428 | | // read the type | 429 | 94.3k | let length = buf.parse_u16()?; | 430 | | | 431 | | Ok(Item { | 432 | 94.3k | index: I::from(index), | 433 | 94.3k | data: buf.take(length as usize)?, | 434 | | }) | 435 | | } else { | 436 | | // miss | 437 | 0 | Err(Error::TypeNotIndexed(index, self.max_index().into())) | 438 | | } | 439 | 109k | } |
Unexecuted instantiation: <pdb2::tpi::ItemFinder<_>>::find |
440 | | } |
441 | | |
442 | | /// An iterator over items in [`TypeInformation`] or [`IdInformation`]. |
443 | | /// |
444 | | /// The TPI and IPI streams are represented internally as a series of records, each of which have a |
445 | | /// length, a kind, and a type-specific field layout. Iteration performance is therefore similar to |
446 | | /// a linked list. |
447 | | #[derive(Debug)] |
448 | | pub struct ItemIter<'t, I> { |
449 | | buf: ParseBuffer<'t>, |
450 | | index: u32, |
451 | | _ph: PhantomData<&'t I>, |
452 | | } |
453 | | |
454 | | impl<'t, I> FallibleIterator for ItemIter<'t, I> |
455 | | where |
456 | | I: ItemIndex, |
457 | | { |
458 | | type Item = Item<'t, I>; |
459 | | type Error = Error; |
460 | | |
461 | 1.17M | fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> { |
462 | | // see if we're at EOF |
463 | 1.17M | if self.buf.is_empty() { |
464 | 14.1k | return Ok(None); |
465 | 1.16M | } |
466 | | |
467 | | // read the length of the next type |
468 | 1.16M | let length = self.buf.parse_u16()? as usize; |
469 | | |
470 | | // validate |
471 | 1.15M | if length < 2 { |
472 | | // this can't be correct |
473 | 13.4k | return Err(Error::TypeTooShort); |
474 | 1.14M | } |
475 | | |
476 | | // grab the type itself |
477 | 1.14M | let type_buf = self.buf.take(length)?; |
478 | 1.14M | let index = self.index; |
479 | | |
480 | 1.14M | self.index += 1; |
481 | | |
482 | | // Done |
483 | 1.14M | Ok(Some(Item { |
484 | 1.14M | index: I::from(index), |
485 | 1.14M | data: type_buf, |
486 | 1.14M | })) |
487 | 1.17M | } <pdb2::tpi::ItemIter<pdb2::common::IdIndex> as fallible_iterator::FallibleIterator>::next Line | Count | Source | 461 | 277k | fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> { | 462 | | // see if we're at EOF | 463 | 277k | if self.buf.is_empty() { | 464 | 3.26k | return Ok(None); | 465 | 274k | } | 466 | | | 467 | | // read the length of the next type | 468 | 274k | let length = self.buf.parse_u16()? as usize; | 469 | | | 470 | | // validate | 471 | 274k | if length < 2 { | 472 | | // this can't be correct | 473 | 434 | return Err(Error::TypeTooShort); | 474 | 274k | } | 475 | | | 476 | | // grab the type itself | 477 | 274k | let type_buf = self.buf.take(length)?; | 478 | 272k | let index = self.index; | 479 | | | 480 | 272k | self.index += 1; | 481 | | | 482 | | // Done | 483 | 272k | Ok(Some(Item { | 484 | 272k | index: I::from(index), | 485 | 272k | data: type_buf, | 486 | 272k | })) | 487 | 277k | } |
<pdb2::tpi::ItemIter<pdb2::common::TypeIndex> as fallible_iterator::FallibleIterator>::next Line | Count | Source | 461 | 897k | fn next(&mut self) -> result::Result<Option<Self::Item>, Self::Error> { | 462 | | // see if we're at EOF | 463 | 897k | if self.buf.is_empty() { | 464 | 10.8k | return Ok(None); | 465 | 886k | } | 466 | | | 467 | | // read the length of the next type | 468 | 886k | let length = self.buf.parse_u16()? as usize; | 469 | | | 470 | | // validate | 471 | 884k | if length < 2 { | 472 | | // this can't be correct | 473 | 12.9k | return Err(Error::TypeTooShort); | 474 | 871k | } | 475 | | | 476 | | // grab the type itself | 477 | 871k | let type_buf = self.buf.take(length)?; | 478 | 868k | let index = self.index; | 479 | | | 480 | 868k | self.index += 1; | 481 | | | 482 | | // Done | 483 | 868k | Ok(Some(Item { | 484 | 868k | index: I::from(index), | 485 | 868k | data: type_buf, | 486 | 868k | })) | 487 | 897k | } |
Unexecuted instantiation: <pdb2::tpi::ItemIter<_> as fallible_iterator::FallibleIterator>::next |
488 | | } |
489 | | |
490 | | /// Zero-copy access to the PDB type stream (TPI). |
491 | | /// |
492 | | /// This stream exposes types, the variants of which are enumerated by [`TypeData`]. See |
493 | | /// [`ItemInformation`] for more information on accessing types. |
494 | | pub type TypeInformation<'s> = ItemInformation<'s, TypeIndex>; |
495 | | |
496 | | /// In-memory index for efficient random-access of [`Type`]s by index. |
497 | | /// |
498 | | /// `TypeFinder` can be obtained via [`TypeInformation::finder`](ItemInformation::finder). See |
499 | | /// [`ItemFinder`] for more information. |
500 | | pub type TypeFinder<'t> = ItemFinder<'t, TypeIndex>; |
501 | | |
502 | | /// An iterator over [`Type`]s returned by [`TypeInformation::iter`](ItemInformation::iter). |
503 | | pub type TypeIter<'t> = ItemIter<'t, TypeIndex>; |
504 | | |
505 | | /// Information on a primitive type, class, or procedure. |
506 | | pub type Type<'t> = Item<'t, TypeIndex>; |
507 | | |
508 | | impl<'t> Item<'t, TypeIndex> { |
509 | | /// Parse this `Type` into `TypeData`. |
510 | | /// |
511 | | /// # Errors |
512 | | /// |
513 | | /// * `Error::UnimplementedTypeKind(kind)` if the type record isn't currently understood by this |
514 | | /// library |
515 | | /// * `Error::UnexpectedEof` if the type record is malformed |
516 | 110k | pub fn parse(&self) -> Result<TypeData<'t>> { |
517 | 110k | if self.index < TypeIndex(0x1000) { |
518 | | // Primitive type |
519 | 14.8k | type_data_for_primitive(self.index) |
520 | | } else { |
521 | 95.6k | let mut buf = ParseBuffer::from(self.data); |
522 | 95.6k | parse_type_data(&mut buf) |
523 | | } |
524 | 110k | } |
525 | | } |
526 | | |
527 | | /// Zero-copy access to the PDB type stream (TPI). |
528 | | /// |
529 | | /// This stream exposes types, the variants of which are enumerated by [`IdData`]. See |
530 | | /// [`ItemInformation`] for more information on accessing types. |
531 | | pub type IdInformation<'s> = ItemInformation<'s, IdIndex>; |
532 | | |
533 | | /// In-memory index for efficient random-access of [`Id`]s by index. |
534 | | /// |
535 | | /// `IdFinder` can be obtained via [`IdInformation::finder`](ItemInformation::finder). See |
536 | | /// [`ItemFinder`] for more information. |
537 | | pub type IdFinder<'t> = ItemFinder<'t, IdIndex>; |
538 | | |
539 | | /// An iterator over [`Id`]s returned by [`IdInformation::iter`](ItemInformation::iter). |
540 | | pub type IdIter<'t> = ItemIter<'t, IdIndex>; |
541 | | |
542 | | /// Information on an inline function, build infos or source references. |
543 | | pub type Id<'t> = Item<'t, IdIndex>; |
544 | | |
545 | | impl<'t> Item<'t, IdIndex> { |
546 | | /// Parse this `Id` into `IdData`. |
547 | | /// |
548 | | /// # Errors |
549 | | /// |
550 | | /// * `Error::UnimplementedTypeKind(kind)` if the id record isn't currently understood by this |
551 | | /// library |
552 | | /// * `Error::UnexpectedEof` if the id record is malformed |
553 | 35.1k | pub fn parse(&self) -> Result<IdData<'t>> { |
554 | 35.1k | ParseBuffer::from(self.data).parse() |
555 | 35.1k | } |
556 | | } |