Coverage Report

Created: 2026-11-11 06:34

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}