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}