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