miniextendr_api/altrep_data/iter/coerce.rs
1//! Iterator-backed ALTREP data adaptors with coercion support.
2//!
3//! Provides `IterIntCoerceData`, `IterRealCoerceData`, and `IterIntFromBoolData`
4//! for iterators that produce values coercible to the target R type, plus the
5//! string/list/complex adaptors (`IterStringData`, `IterListData`,
6//! `IterComplexData`).
7//!
8//! See the iterator-adaptor section in the [`altrep_data`](crate::altrep_data)
9//! module docs for how to expose
10//! these adaptors to R (wrap in a `#[derive(Altrep*)]` + `#[altrep(manual)]`
11//! struct).
12
13use super::IterState;
14use crate::SEXP;
15use crate::altrep_data::{
16 AltComplexData, AltIntegerData, AltListData, AltRealData, AltStringData, AltrepLen, fill_region,
17};
18
19/// Iterator-backed integer vector data adaptor with coercion from any integer-like type.
20///
21/// Wraps an iterator producing values that coerce to `i32` (e.g., `u16`, `i8`, etc.)
22/// and implements the data-level traits ([`AltrepLen`] + [`AltIntegerData`]).
23/// To expose it to R, wrap it in a `#[derive(AltrepInteger)]` +
24/// `#[altrep(manual)]` struct (see the iterator module documentation).
25///
26/// # Example
27///
28/// ```ignore
29/// use miniextendr_api::altrep_data::IterIntCoerceData;
30///
31/// // Create from an iterator of u16 values
32/// let iter = (0..10u16).map(|x| x * 100);
33/// let data = IterIntCoerceData::from_iter(iter, 10);
34/// // Values are coerced from u16 to i32 when accessed
35/// ```
36pub struct IterIntCoerceData<I, T>
37where
38 I: Iterator<Item = T>,
39 T: crate::coerce::Coerce<i32> + Copy,
40{
41 state: IterState<I, T>,
42}
43
44impl<I, T> IterIntCoerceData<I, T>
45where
46 I: Iterator<Item = T>,
47 T: crate::coerce::Coerce<i32> + Copy,
48{
49 /// Create from an iterator with explicit length.
50 pub fn from_iter(iter: I, len: usize) -> Self {
51 Self {
52 state: IterState::new(iter, len),
53 }
54 }
55}
56
57impl<I, T> IterIntCoerceData<I, T>
58where
59 I: ExactSizeIterator<Item = T>,
60 T: crate::coerce::Coerce<i32> + Copy,
61{
62 /// Create from an ExactSizeIterator (length auto-detected).
63 pub fn from_exact_iter(iter: I) -> Self {
64 Self {
65 state: IterState::from_exact_size(iter),
66 }
67 }
68}
69
70impl<I, T> AltrepLen for IterIntCoerceData<I, T>
71where
72 I: Iterator<Item = T>,
73 T: crate::coerce::Coerce<i32> + Copy,
74{
75 fn len(&self) -> usize {
76 self.state.len()
77 }
78}
79
80impl<I, T> AltIntegerData for IterIntCoerceData<I, T>
81where
82 I: Iterator<Item = T>,
83 T: crate::coerce::Coerce<i32> + Copy,
84{
85 fn elt(&self, i: usize) -> i32 {
86 self.state
87 .get_element(i)
88 .map(|val| val.coerce())
89 .unwrap_or(crate::altrep_traits::NA_INTEGER)
90 }
91
92 fn as_slice(&self) -> Option<&[i32]> {
93 // Can't return slice of i32 when cached values are type T
94 // Would need a separate coerced cache
95 None
96 }
97
98 fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
99 fill_region(start, len, self.len(), buf, |idx| self.elt(idx))
100 }
101}
102
103/// Iterator-backed real vector data adaptor with coercion from any float-like type.
104///
105/// Wraps an iterator producing values that coerce to `f64` (e.g., `f32`, integer types).
106///
107/// # Example
108///
109/// ```ignore
110/// use miniextendr_api::altrep_data::IterRealCoerceData;
111///
112/// // Create from an iterator of f32 values
113/// let iter = (0..5).map(|x| x as f32 * 1.5);
114/// let data = IterRealCoerceData::from_iter(iter, 5);
115/// // Values are coerced from f32 to f64 when accessed
116/// ```
117pub struct IterRealCoerceData<I, T>
118where
119 I: Iterator<Item = T>,
120 T: crate::coerce::Coerce<f64> + Copy,
121{
122 state: IterState<I, T>,
123}
124
125impl<I, T> IterRealCoerceData<I, T>
126where
127 I: Iterator<Item = T>,
128 T: crate::coerce::Coerce<f64> + Copy,
129{
130 /// Create from an iterator with explicit length.
131 pub fn from_iter(iter: I, len: usize) -> Self {
132 Self {
133 state: IterState::new(iter, len),
134 }
135 }
136}
137
138impl<I, T> IterRealCoerceData<I, T>
139where
140 I: ExactSizeIterator<Item = T>,
141 T: crate::coerce::Coerce<f64> + Copy,
142{
143 /// Create from an ExactSizeIterator (length auto-detected).
144 pub fn from_exact_iter(iter: I) -> Self {
145 Self {
146 state: IterState::from_exact_size(iter),
147 }
148 }
149}
150
151impl<I, T> AltrepLen for IterRealCoerceData<I, T>
152where
153 I: Iterator<Item = T>,
154 T: crate::coerce::Coerce<f64> + Copy,
155{
156 fn len(&self) -> usize {
157 self.state.len()
158 }
159}
160
161impl<I, T> AltRealData for IterRealCoerceData<I, T>
162where
163 I: Iterator<Item = T>,
164 T: crate::coerce::Coerce<f64> + Copy,
165{
166 fn elt(&self, i: usize) -> f64 {
167 self.state
168 .get_element(i)
169 .map(|val| val.coerce())
170 .unwrap_or(f64::NAN)
171 }
172
173 fn as_slice(&self) -> Option<&[f64]> {
174 // Can't return slice of f64 when cached values are type T
175 None
176 }
177
178 fn get_region(&self, start: usize, len: usize, buf: &mut [f64]) -> usize {
179 fill_region(start, len, self.len(), buf, |idx| self.elt(idx))
180 }
181}
182
183/// Iterator-backed integer vector data adaptor with coercion from bool.
184///
185/// Wraps an iterator producing `bool` values that coerce to `i32`.
186/// Useful for converting boolean iterators to integer vectors.
187pub struct IterIntFromBoolData<I>
188where
189 I: Iterator<Item = bool>,
190{
191 state: IterState<I, bool>,
192}
193
194impl<I> IterIntFromBoolData<I>
195where
196 I: Iterator<Item = bool>,
197{
198 /// Create from an iterator with explicit length.
199 pub fn from_iter(iter: I, len: usize) -> Self {
200 Self {
201 state: IterState::new(iter, len),
202 }
203 }
204}
205
206impl<I> IterIntFromBoolData<I>
207where
208 I: ExactSizeIterator<Item = bool>,
209{
210 /// Create from an ExactSizeIterator (length auto-detected).
211 pub fn from_exact_iter(iter: I) -> Self {
212 Self {
213 state: IterState::from_exact_size(iter),
214 }
215 }
216}
217
218impl<I> AltrepLen for IterIntFromBoolData<I>
219where
220 I: Iterator<Item = bool>,
221{
222 fn len(&self) -> usize {
223 self.state.len()
224 }
225}
226
227impl<I> AltIntegerData for IterIntFromBoolData<I>
228where
229 I: Iterator<Item = bool>,
230{
231 fn elt(&self, i: usize) -> i32 {
232 use crate::coerce::Coerce;
233 self.state
234 .get_element(i)
235 .map(|val| val.coerce())
236 .unwrap_or(crate::altrep_traits::NA_INTEGER)
237 }
238
239 fn as_slice(&self) -> Option<&[i32]> {
240 None
241 }
242
243 fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
244 fill_region(start, len, self.len(), buf, |idx| self.elt(idx))
245 }
246}
247
248/// Iterator-backed string vector data adaptor.
249///
250/// Wraps an iterator producing `String` values and implements the data-level
251/// traits ([`AltrepLen`] + [`AltStringData`]) for backing an ALTREP character
252/// vector. To expose it to R, wrap it in a `#[derive(AltrepString)]` +
253/// `#[altrep(manual)]` struct (see the iterator module documentation).
254///
255/// # Performance Warning
256///
257/// Unlike other `Iter*Data` types, **accessing ANY element forces full materialization
258/// of the entire iterator**. This is because R's `AltStringData::elt()` returns a borrowed
259/// `&str`, which requires stable storage. The internal `RefCell` cannot provide the required
260/// lifetime, so all strings must be materialized upfront.
261///
262/// This means:
263/// - `elt(0)` on a million-element iterator will generate ALL million strings
264/// - There is no lazy evaluation benefit for string iterators
265/// - Memory usage equals the full vector regardless of access patterns
266///
267/// For truly lazy string ALTREP, consider implementing a custom type that stores
268/// strings in a way that allows borrowing without full materialization (e.g., arena
269/// allocation or caching generated strings incrementally).
270///
271/// # Example
272///
273/// ```ignore
274/// use miniextendr_api::altrep_data::IterStringData;
275///
276/// let iter = (0..5).map(|x| format!("item_{}", x));
277/// let data = IterStringData::from_iter(iter, 5);
278/// // First access to ANY element will materialize all 5 strings
279/// ```
280pub struct IterStringData<I>
281where
282 I: Iterator<Item = String>,
283{
284 state: IterState<I, String>,
285}
286
287impl<I> IterStringData<I>
288where
289 I: Iterator<Item = String>,
290{
291 /// Create from an iterator with explicit length.
292 pub fn from_iter(iter: I, len: usize) -> Self {
293 Self {
294 state: IterState::new(iter, len),
295 }
296 }
297}
298
299impl<I> IterStringData<I>
300where
301 I: ExactSizeIterator<Item = String>,
302{
303 /// Create from an ExactSizeIterator (length auto-detected).
304 pub fn from_exact_iter(iter: I) -> Self {
305 Self {
306 state: IterState::from_exact_size(iter),
307 }
308 }
309}
310
311impl<I> AltrepLen for IterStringData<I>
312where
313 I: Iterator<Item = String>,
314{
315 fn len(&self) -> usize {
316 self.state.len()
317 }
318}
319
320impl<I> AltStringData for IterStringData<I>
321where
322 I: Iterator<Item = String>,
323{
324 fn elt(&self, i: usize) -> Option<&str> {
325 // Materialize to get stable storage for &str references
326 // This is necessary because we can't return &str from RefCell borrows
327 let materialized = self.state.materialize_all();
328 materialized.get(i).map(|s| s.as_str())
329 }
330}
331
332/// Iterator-backed list vector data adaptor.
333///
334/// Wraps an iterator producing R `SEXP` values and implements the data-level
335/// traits ([`AltrepLen`] + [`AltListData`]) for backing an ALTREP list. To
336/// expose it to R, wrap it in a `#[derive(AltrepList)]` + `#[altrep(manual)]`
337/// struct (see the iterator module documentation).
338///
339/// # Safety
340///
341/// The iterator must produce valid, protected SEXP values. Each SEXP must remain
342/// protected for the lifetime of the ALTREP object.
343///
344/// # Example
345///
346/// ```ignore
347/// use miniextendr_api::altrep_data::IterListData;
348/// use miniextendr_api::IntoR;
349///
350/// let iter = (0..5).map(|x| vec![x, x+1, x+2].into_sexp());
351/// let data = IterListData::from_iter(iter, 5);
352/// ```
353pub struct IterListData<I>
354where
355 I: Iterator<Item = SEXP>,
356{
357 state: IterState<I, SEXP>,
358}
359
360impl<I> IterListData<I>
361where
362 I: Iterator<Item = SEXP>,
363{
364 /// Create from an iterator with explicit length.
365 ///
366 /// # Safety
367 ///
368 /// The iterator must produce valid, protected SEXP values.
369 pub fn from_iter(iter: I, len: usize) -> Self {
370 Self {
371 state: IterState::new(iter, len),
372 }
373 }
374}
375
376impl<I> IterListData<I>
377where
378 I: ExactSizeIterator<Item = SEXP>,
379{
380 /// Create from an ExactSizeIterator (length auto-detected).
381 ///
382 /// # Safety
383 ///
384 /// The iterator must produce valid, protected SEXP values.
385 pub fn from_exact_iter(iter: I) -> Self {
386 Self {
387 state: IterState::from_exact_size(iter),
388 }
389 }
390}
391
392impl<I> AltrepLen for IterListData<I>
393where
394 I: Iterator<Item = SEXP>,
395{
396 fn len(&self) -> usize {
397 self.state.len()
398 }
399}
400
401impl<I> AltListData for IterListData<I>
402where
403 I: Iterator<Item = SEXP>,
404{
405 fn elt(&self, i: usize) -> SEXP {
406 use crate::SEXP;
407 self.state.get_element(i).unwrap_or(SEXP::nil())
408 }
409}
410
411/// Iterator-backed complex number vector data adaptor.
412///
413/// Wraps an iterator producing `Rcomplex` values and implements the data-level
414/// traits ([`AltrepLen`] + [`AltComplexData`]) for backing an ALTREP complex
415/// vector. To expose it to R, wrap it in a `#[derive(AltrepComplex)]` +
416/// `#[altrep(manual)]` struct (see the iterator module documentation).
417///
418/// # Example
419///
420/// ```ignore
421/// use miniextendr_api::altrep_data::IterComplexData;
422/// use miniextendr_api::Rcomplex;
423///
424/// let iter = (0..5).map(|x| Rcomplex { r: x as f64, i: (x * 2) as f64 });
425/// let data = IterComplexData::from_iter(iter, 5);
426/// ```
427pub struct IterComplexData<I>
428where
429 I: Iterator<Item = crate::Rcomplex>,
430{
431 state: IterState<I, crate::Rcomplex>,
432}
433
434impl<I> IterComplexData<I>
435where
436 I: Iterator<Item = crate::Rcomplex>,
437{
438 /// Create from an iterator with explicit length.
439 pub fn from_iter(iter: I, len: usize) -> Self {
440 Self {
441 state: IterState::new(iter, len),
442 }
443 }
444}
445
446impl<I> IterComplexData<I>
447where
448 I: ExactSizeIterator<Item = crate::Rcomplex>,
449{
450 /// Create from an ExactSizeIterator (length auto-detected).
451 pub fn from_exact_iter(iter: I) -> Self {
452 Self {
453 state: IterState::from_exact_size(iter),
454 }
455 }
456}
457
458impl<I> AltrepLen for IterComplexData<I>
459where
460 I: Iterator<Item = crate::Rcomplex>,
461{
462 fn len(&self) -> usize {
463 self.state.len()
464 }
465}
466
467impl<I> AltComplexData for IterComplexData<I>
468where
469 I: Iterator<Item = crate::Rcomplex>,
470{
471 fn elt(&self, i: usize) -> crate::Rcomplex {
472 self.state.get_element(i).unwrap_or(crate::Rcomplex {
473 r: f64::NAN,
474 i: f64::NAN,
475 })
476 }
477
478 fn as_slice(&self) -> Option<&[crate::Rcomplex]> {
479 self.state.as_materialized()
480 }
481
482 fn get_region(&self, start: usize, len: usize, buf: &mut [crate::Rcomplex]) -> usize {
483 fill_region(start, len, self.len(), buf, |idx| self.elt(idx))
484 }
485}