Skip to main content

windows_collections/
buffered_iterator.rs

1extern crate alloc;
2
3use super::IIterator;
4use alloc::vec::Vec;
5
6/// Elements per `GetMany` block, sized to keep the buffer in the 1-2 KB range
7/// regardless of element size: 128 elements (~1 KB pointer-sized), fewer for
8/// large value structs. A traversal makes roughly `count / block` virtual calls
9/// instead of three per element.
10fn block<T: windows_core::RuntimeType>() -> usize {
11    (2048 / size_of::<T::Default>()).clamp(1, 128)
12}
13
14/// An iterator that reads elements from an [`IIterator`] in batches via
15/// `GetMany` rather than one at a time.
16///
17/// The naive [`IIterator`] iteration calls `HasCurrent`, `Current`, and
18/// `MoveNext` across the ABI for every element. `BufferedIterator` instead
19/// fills a small buffer with a single `GetMany` call and yields from it, cutting
20/// the per-element virtual-call cost by orders of magnitude. This is the iterator
21/// produced when a collection is iterated directly (for example `for value in
22/// &vector`).
23pub struct BufferedIterator<T: windows_core::RuntimeType + 'static> {
24    iterator: IIterator<T>,
25    buffer: Vec<T::Default>,
26    index: usize,
27    len: usize,
28}
29
30impl<T: windows_core::RuntimeType + 'static> BufferedIterator<T> {
31    pub fn new(iterator: IIterator<T>) -> Self {
32        // A zeroed default is valid for every WinRT `Default` type (a null
33        // interface/string or a zero scalar). `GetMany` writes into and `Drop`
34        // releases these values, so the buffer is initialized, not uninhabited.
35        let mut buffer = Vec::new();
36        buffer.resize_with(block::<T>(), || unsafe { core::mem::zeroed() });
37        Self {
38            iterator,
39            buffer,
40            index: 0,
41            len: 0,
42        }
43    }
44}
45
46impl<T: windows_core::RuntimeType + 'static> Iterator for BufferedIterator<T> {
47    type Item = T;
48
49    fn next(&mut self) -> Option<Self::Item> {
50        if self.index >= self.len {
51            self.index = 0;
52            self.len = self.iterator.GetMany(&mut self.buffer).unwrap_or(0) as usize;
53            self.len = self.len.min(self.buffer.len());
54            if self.len == 0 {
55                return None;
56            }
57        }
58
59        // Move the element out of the buffer rather than cloning it, leaving a zeroed slot
60        // behind. For interface elements (such as the `IKeyValuePair` yielded by map iteration)
61        // this hands the buffer's existing reference to the caller, skipping the `AddRef` a
62        // clone would take and the matching `Release` when the slot is later overwritten. Slots
63        // left unconsumed (early-drop, or beyond a short `GetMany`) are released by the `Vec`.
64        let slot = core::mem::replace(&mut self.buffer[self.index], unsafe { core::mem::zeroed() });
65        self.index += 1;
66        T::from_default_owned(slot).ok()
67    }
68}