Skip to main content

msft_typelib/
iterators.rs

1//! Iterators for traversing MSFT type library structures.
2//!
3//! Most iterators walk fixed-size or self-describing entries within a
4//! segment.  Two use linked-list chains: [`ImplTypeIter`] follows
5//! `onext` pointers in the reference table, and [`CustDataIter`]
6//! follows `next` pointers in the CDGuids directory.
7
8use crate::{
9    CustDataEntry, Error, FuncRecord, GuidEntry, ImpFile, ImpInfo, NameEntry, ParameterInfo,
10    RefRecord, TypeInfoEntry, TypeLib, VarRecord,
11    util::{read_i32_le, read_u16_le, read_u32_le},
12};
13
14/// Iterator over [`TypeInfoEntry`] values in a type library.
15///
16/// Yields `Result<TypeInfoEntry, Error>` for each entry (index `0..typeinfo_count`).
17pub struct TypeInfoIter<'a> {
18    lib: &'a TypeLib<'a>,
19    index: usize,
20}
21
22impl<'a> TypeInfoIter<'a> {
23    /// Creates a new iterator starting at index 0.
24    pub(crate) fn new(lib: &'a TypeLib<'a>) -> Self {
25        Self { lib, index: 0 }
26    }
27}
28
29impl<'a> Iterator for TypeInfoIter<'a> {
30    type Item = Result<TypeInfoEntry<'a>, Error>;
31
32    fn next(&mut self) -> Option<Self::Item> {
33        if self.index >= self.lib.typeinfo_count() {
34            return None;
35        }
36        let result = self.lib.typeinfo(self.index);
37        self.index += 1;
38        Some(result)
39    }
40
41    fn size_hint(&self) -> (usize, Option<usize>) {
42        let rem = self.lib.typeinfo_count() - self.index;
43        (rem, Some(rem))
44    }
45}
46
47/// Iterator over [`FuncRecord`] values within a TypeInfo's func/var data block.
48pub struct FuncIter<'a> {
49    data: &'a [u8],
50    pos: usize,
51    end: usize,
52    remaining: usize,
53}
54
55impl<'a> FuncIter<'a> {
56    /// Creates a new iterator over the `[pos..end]` byte range.
57    pub(crate) fn new(data: &'a [u8], pos: usize, end: usize, remaining: usize) -> Self {
58        Self {
59            data,
60            pos,
61            end,
62            remaining,
63        }
64    }
65}
66
67impl<'a> Iterator for FuncIter<'a> {
68    type Item = FuncRecord<'a>;
69
70    fn next(&mut self) -> Option<Self::Item> {
71        if self.remaining == 0 || self.pos >= self.end {
72            return None;
73        }
74        let info_raw = read_u32_le(self.data, self.pos)?;
75        let size = (info_raw & 0xFFFF) as usize;
76        if size < FuncRecord::BASE_SIZE {
77            return None;
78        }
79        let end = self.pos.checked_add(size)?;
80        if end > self.data.len() {
81            return None;
82        }
83        let record = FuncRecord::new(&self.data[self.pos..end]);
84        self.pos = end;
85        self.remaining -= 1;
86        Some(record)
87    }
88
89    fn size_hint(&self) -> (usize, Option<usize>) {
90        (0, Some(self.remaining))
91    }
92}
93
94/// Iterator over [`VarRecord`] values within a TypeInfo's func/var data block.
95pub struct VarIter<'a> {
96    data: &'a [u8],
97    pos: usize,
98    end: usize,
99    remaining: usize,
100}
101
102impl<'a> VarIter<'a> {
103    /// Creates a new iterator over the `[pos..end]` byte range.
104    pub(crate) fn new(data: &'a [u8], pos: usize, end: usize, remaining: usize) -> Self {
105        Self {
106            data,
107            pos,
108            end,
109            remaining,
110        }
111    }
112}
113
114impl<'a> Iterator for VarIter<'a> {
115    type Item = VarRecord<'a>;
116
117    fn next(&mut self) -> Option<Self::Item> {
118        if self.remaining == 0 || self.pos >= self.end {
119            return None;
120        }
121        let info_raw = read_u32_le(self.data, self.pos)?;
122        let size = (info_raw & 0xFFFF) as usize;
123        if size < VarRecord::BASE_SIZE {
124            return None;
125        }
126        let end = self.pos.checked_add(size)?;
127        if end > self.data.len() {
128            return None;
129        }
130        let record = VarRecord::new(&self.data[self.pos..end]);
131        self.pos = end;
132        self.remaining -= 1;
133        Some(record)
134    }
135
136    fn size_hint(&self) -> (usize, Option<usize>) {
137        (0, Some(self.remaining))
138    }
139}
140
141/// Iterator over [`ParameterInfo`] entries within a [`FuncRecord`].
142pub struct ParamIter<'a> {
143    data: &'a [u8],
144    pos: usize,
145    remaining: usize,
146}
147
148impl<'a> ParamIter<'a> {
149    /// Creates a new iterator starting at `pos` within `data`.
150    pub(crate) fn new(data: &'a [u8], pos: usize, remaining: usize) -> Self {
151        Self {
152            data,
153            pos,
154            remaining,
155        }
156    }
157}
158
159impl<'a> Iterator for ParamIter<'a> {
160    type Item = ParameterInfo<'a>;
161
162    fn next(&mut self) -> Option<Self::Item> {
163        if self.remaining == 0 {
164            return None;
165        }
166        let end = self.pos.checked_add(ParameterInfo::SIZE)?;
167        if end > self.data.len() {
168            return None;
169        }
170        let param = ParameterInfo::new(&self.data[self.pos..end]);
171        self.pos = end;
172        self.remaining -= 1;
173        Some(param)
174    }
175
176    fn size_hint(&self) -> (usize, Option<usize>) {
177        (self.remaining, Some(self.remaining))
178    }
179}
180
181impl ExactSizeIterator for ParamIter<'_> {}
182
183/// Iterator over [`GuidEntry`] values in the GUID table segment.
184pub struct GuidEntryIter<'a> {
185    data: &'a [u8],
186    pos: usize,
187    end: usize,
188}
189
190impl<'a> GuidEntryIter<'a> {
191    /// Creates a new iterator over the `[pos..end]` byte range.
192    pub(crate) fn new(data: &'a [u8], pos: usize, end: usize) -> Self {
193        Self { data, pos, end }
194    }
195}
196
197impl<'a> Iterator for GuidEntryIter<'a> {
198    type Item = GuidEntry<'a>;
199
200    fn next(&mut self) -> Option<Self::Item> {
201        let end = self.pos.checked_add(GuidEntry::SIZE)?;
202        if end > self.end || end > self.data.len() {
203            return None;
204        }
205        let entry = GuidEntry::new(&self.data[self.pos..end]);
206        self.pos = end;
207        Some(entry)
208    }
209}
210
211/// Iterator over entries in the name table segment.
212///
213/// Yields [`NameEntry`] values containing the name string, its byte offset,
214/// and the `hreftype` linking the name to a TypeInfo.
215pub struct NameIter<'a> {
216    data: &'a [u8],
217    pos: usize,
218    end: usize,
219    seg_start: usize,
220}
221
222impl<'a> NameIter<'a> {
223    /// Creates a new iterator over the `[pos..end]` byte range.
224    pub(crate) fn new(data: &'a [u8], pos: usize, end: usize, seg_start: usize) -> Self {
225        Self {
226            data,
227            pos,
228            end,
229            seg_start,
230        }
231    }
232}
233
234impl<'a> Iterator for NameIter<'a> {
235    type Item = NameEntry<'a>;
236
237    fn next(&mut self) -> Option<Self::Item> {
238        // Header: hreftype(4) + next_hash(4) + namelen(1) + flags(1) + hashcode(2) = 12
239        let header_end = self.pos.checked_add(12)?;
240        if header_end > self.end || header_end > self.data.len() {
241            return None;
242        }
243        let hreftype = read_i32_le(self.data, self.pos)?;
244        let next_hash = read_i32_le(self.data, self.pos + 4)?;
245        let namelen = *self.data.get(self.pos + 8)? as usize;
246        let flags = *self.data.get(self.pos + 9)?;
247        let hashcode = read_u16_le(self.data, self.pos + 10)?;
248        let name_start = header_end;
249        let name_end = name_start.checked_add(namelen)?;
250        if name_end > self.data.len() {
251            return None;
252        }
253        let offset = self.pos - self.seg_start;
254        let name = std::str::from_utf8(self.data.get(name_start..name_end)?).ok()?;
255        // Advance past name + padding to 4-byte alignment
256        let padded = (namelen + 3) & !3;
257        self.pos = name_start.checked_add(padded)?;
258        Some(NameEntry::new(
259            offset, name, hreftype, next_hash, flags, hashcode,
260        ))
261    }
262}
263
264/// Iterator over implemented interfaces for a coclass (via `pRefTab` chain).
265///
266/// Yields [`RefRecord`] entries following the linked-list chain until
267/// an `onext` value of `-1` is reached.
268pub struct ImplTypeIter<'a> {
269    data: &'a [u8],
270    seg_offset: usize,
271    next_offset: i32,
272}
273
274impl<'a> ImplTypeIter<'a> {
275    /// Creates a new iterator starting at `next_offset` in the reference table.
276    pub(crate) fn new(data: &'a [u8], seg_offset: usize, next_offset: i32) -> Self {
277        Self {
278            data,
279            seg_offset,
280            next_offset,
281        }
282    }
283}
284
285impl<'a> Iterator for ImplTypeIter<'a> {
286    type Item = RefRecord<'a>;
287
288    fn next(&mut self) -> Option<Self::Item> {
289        if self.next_offset < 0 {
290            return None;
291        }
292        let abs = self.seg_offset.checked_add(self.next_offset as usize)?;
293        let end = abs.checked_add(RefRecord::SIZE)?;
294        if end > self.data.len() {
295            return None;
296        }
297        let rec = RefRecord::new(&self.data[abs..end]);
298        self.next_offset = rec.onext();
299        Some(rec)
300    }
301}
302
303/// Iterator over [`ImpInfo`] entries in the import-info segment.
304pub struct ImpInfoIter<'a> {
305    data: &'a [u8],
306    pos: usize,
307    end: usize,
308}
309
310impl<'a> ImpInfoIter<'a> {
311    /// Creates a new iterator over the `[pos..end]` byte range.
312    pub(crate) fn new(data: &'a [u8], pos: usize, end: usize) -> Self {
313        Self { data, pos, end }
314    }
315}
316
317impl<'a> Iterator for ImpInfoIter<'a> {
318    type Item = ImpInfo<'a>;
319
320    fn next(&mut self) -> Option<Self::Item> {
321        let end = self.pos.checked_add(ImpInfo::SIZE)?;
322        if end > self.end || end > self.data.len() {
323            return None;
324        }
325        let entry = ImpInfo::new(&self.data[self.pos..end]);
326        self.pos = end;
327        Some(entry)
328    }
329}
330
331/// Iterator over [`ImpFile`] entries in the import-files segment (segment 2).
332///
333/// Entries are variable-length; each entry's size is derived from its
334/// `size_field`.
335pub struct ImpFileIter<'a> {
336    data: &'a [u8],
337    pos: usize,
338    end: usize,
339}
340
341impl<'a> ImpFileIter<'a> {
342    /// Creates a new iterator over variable-length entries in `[pos..end]`.
343    pub(crate) fn new(data: &'a [u8], pos: usize, end: usize) -> Self {
344        Self { data, pos, end }
345    }
346}
347
348impl<'a> Iterator for ImpFileIter<'a> {
349    type Item = ImpFile<'a>;
350
351    fn next(&mut self) -> Option<Self::Item> {
352        if self.pos.checked_add(ImpFile::HEADER_SIZE)? > self.end {
353            return None;
354        }
355        let size_field = read_u16_le(self.data, self.pos + 0x0C)?;
356        let name_len = (size_field >> 2) as usize;
357        let unpadded = ImpFile::HEADER_SIZE + name_len;
358        let entry_size = (unpadded + 3) & !3;
359        let end = self.pos.checked_add(entry_size)?;
360        if end > self.end || end > self.data.len() {
361            return None;
362        }
363        let entry = ImpFile::new(&self.data[self.pos..end]);
364        self.pos = end;
365        Some(entry)
366    }
367}
368
369/// Iterator that walks a custom data chain in the CDGuids directory (segment 12).
370///
371/// Each [`CustDataEntry`] links to the next via its `next` field.
372/// The chain ends when `next` is `-1`.
373pub struct CustDataIter<'a> {
374    data: &'a [u8],
375    seg_offset: usize,
376    next_offset: i32,
377}
378
379impl<'a> CustDataIter<'a> {
380    /// Creates a new iterator starting at `next_offset` in the CDGuids directory.
381    pub(crate) fn new(data: &'a [u8], seg_offset: usize, next_offset: i32) -> Self {
382        Self {
383            data,
384            seg_offset,
385            next_offset,
386        }
387    }
388}
389
390impl<'a> Iterator for CustDataIter<'a> {
391    type Item = CustDataEntry<'a>;
392
393    fn next(&mut self) -> Option<Self::Item> {
394        if self.next_offset < 0 {
395            return None;
396        }
397        let abs = self.seg_offset.checked_add(self.next_offset as usize)?;
398        let end = abs.checked_add(CustDataEntry::SIZE)?;
399        if end > self.data.len() {
400            return None;
401        }
402        let entry = CustDataEntry::new(&self.data[abs..end]);
403        self.next_offset = entry.next();
404        Some(entry)
405    }
406}