Skip to main content

miniextendr_api/altrep_data/
builtins.rs

1//! Built-in ALTREP data implementations for standard Rust types.
2//!
3//! Provides `AltIntegerData`, `AltRealData`, `AltLogicalData`, `AltRawData`,
4//! `AltStringData`, and `AltComplexData` implementations for:
5//!
6//! - `Vec<T>` — owned vector (most common)
7//! - `Box<[T]>` — boxed slice
8//! - `Range<T>` — lazy arithmetic sequences (integer, real)
9//! - `&'static [T]` — borrowed slices (zero-copy from static data)
10//! - `[T; N]` — fixed-size arrays
11
12use std::borrow::Cow;
13use std::ops::Range;
14
15use crate::altrep_traits::NA_REAL;
16use crate::{Rcomplex, SEXP};
17
18use super::{
19    AltComplexData, AltIntegerData, AltLogicalData, AltRawData, AltRealData, AltStringData,
20    AltrepDataptr, AltrepLen, AltrepSerialize, Logical, Sortedness,
21};
22
23// region: Helper macros to reduce repetition
24
25/// Implement AltrepLen for Vec<$elem>
26macro_rules! impl_len_vec {
27    ($elem:ty) => {
28        impl AltrepLen for Vec<$elem> {
29            fn len(&self) -> usize {
30                Vec::len(self)
31            }
32        }
33    };
34}
35
36/// Implement AltrepLen for Box<[$elem]>
37macro_rules! impl_len_boxed {
38    ($elem:ty) => {
39        impl AltrepLen for Box<[$elem]> {
40            fn len(&self) -> usize {
41                <[$elem]>::len(self)
42            }
43        }
44    };
45}
46
47/// Implement AltrepLen for [$elem; N]
48macro_rules! impl_len_array {
49    ($elem:ty) => {
50        impl<const N: usize> AltrepLen for [$elem; N] {
51            fn len(&self) -> usize {
52                N
53            }
54        }
55    };
56}
57
58/// Implement AltrepLen for &[$elem]
59macro_rules! impl_len_slice {
60    ($elem:ty) => {
61        impl AltrepLen for &[$elem] {
62            fn len(&self) -> usize {
63                <[$elem]>::len(self)
64            }
65        }
66    };
67}
68
69/// Implement AltrepDataptr for Vec<$elem> (types with direct memory access)
70macro_rules! impl_dataptr_vec {
71    ($elem:ty) => {
72        impl AltrepDataptr<$elem> for Vec<$elem> {
73            fn dataptr(&mut self, _writable: bool) -> Option<*mut $elem> {
74                Some(self.as_mut_ptr())
75            }
76
77            fn dataptr_or_null(&self) -> Option<*const $elem> {
78                Some(self.as_ptr())
79            }
80        }
81    };
82}
83
84/// Implement AltrepDataptr for Box<[$elem]> (types with direct memory access)
85macro_rules! impl_dataptr_boxed {
86    ($elem:ty) => {
87        impl AltrepDataptr<$elem> for Box<[$elem]> {
88            fn dataptr(&mut self, _writable: bool) -> Option<*mut $elem> {
89                Some(self.as_mut_ptr())
90            }
91
92            fn dataptr_or_null(&self) -> Option<*const $elem> {
93                Some(self.as_ptr())
94            }
95        }
96    };
97}
98
99/// Implement AltrepSerialize for types that can be cloned and converted to/from R.
100///
101/// This serializes by converting to a native R vector, ensuring the data survives
102/// even if the Rust package isn't loaded when unserializing.
103macro_rules! impl_serialize {
104    ($ty:ty) => {
105        impl AltrepSerialize for $ty {
106            fn serialized_state(&self) -> SEXP {
107                use crate::into_r::IntoR;
108                self.clone().into_sexp()
109            }
110
111            fn unserialize(state: SEXP) -> Option<Self> {
112                use crate::from_r::TryFromSexp;
113                <$ty>::try_from_sexp(state).ok()
114            }
115        }
116    };
117}
118// endregion
119
120// region: Slice-backed family macros
121//
122// The numeric / raw / complex ALTREP families share byte-identical bodies
123// across every container shape that derefs to `[T]` (`Vec<T>`, `Box<[T]>`,
124// `&[T]`, `Cow<'static, [T]>`). The element type is the only axis that varies
125// the *behaviour* (NA sentinel + statistic semantics); the container shape only
126// varies how the slice is spelled. Each macro below collapses one element
127// family across all slice-like containers by writing every method body against
128// the deref'd `&[$elem]` slice.
129//
130// `[T; N]` arrays are intentionally NOT routed through these macros: they need
131// `impl<const N: usize>` (not expressible via `$ty:ty`) and they deliberately
132// omit `sum`/`min`/`max` (falling back to R's defaults). `Range<T>` is lazy and
133// has its own arithmetic-sequence bodies. The string families have per-type
134// `elt`/`no_na` differences and stay hand-written.
135
136/// `AltIntegerData` for any container that derefs to `[i32]`.
137///
138/// `s` binds the underlying `&[i32]` uniformly across `Vec` / `Box<[T]>` /
139/// `&[T]` / `Cow`, sidestepping the `self.len()` ambiguity between
140/// `AltrepLen::len` and the inherent slice/`Vec` `len`.
141macro_rules! impl_altinteger_slice {
142    ($ty:ty) => {
143        impl AltIntegerData for $ty {
144            fn elt(&self, i: usize) -> i32 {
145                self[i]
146            }
147
148            fn as_slice(&self) -> Option<&[i32]> {
149                let s: &[i32] = self;
150                Some(s)
151            }
152
153            fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
154                let s: &[i32] = self;
155                let end = (start + len).min(s.len());
156                let actual_len = end.saturating_sub(start);
157                if actual_len > 0 {
158                    buf[..actual_len].copy_from_slice(&s[start..end]);
159                }
160                actual_len
161            }
162
163            fn no_na(&self) -> Option<bool> {
164                let s: &[i32] = self;
165                Some(!s.contains(&i32::MIN))
166            }
167
168            fn sum(&self, na_rm: bool) -> Option<i64> {
169                let s: &[i32] = self;
170                let mut sum: i64 = 0;
171                for &x in s.iter() {
172                    if x == i32::MIN {
173                        if !na_rm {
174                            return None; // NA propagates
175                        }
176                    } else {
177                        sum += x as i64;
178                    }
179                }
180                Some(sum)
181            }
182
183            fn min(&self, na_rm: bool) -> Option<i32> {
184                let s: &[i32] = self;
185                let mut min = i32::MAX;
186                let mut found = false;
187                for &x in s.iter() {
188                    if x == i32::MIN {
189                        if !na_rm {
190                            return None;
191                        }
192                    } else {
193                        found = true;
194                        min = min.min(x);
195                    }
196                }
197                if found { Some(min) } else { None }
198            }
199
200            fn max(&self, na_rm: bool) -> Option<i32> {
201                let s: &[i32] = self;
202                let mut max = i32::MIN + 1; // i32::MIN is the NA sentinel
203                let mut found = false;
204                for &x in s.iter() {
205                    if x == i32::MIN {
206                        if !na_rm {
207                            return None;
208                        }
209                    } else {
210                        found = true;
211                        max = max.max(x);
212                    }
213                }
214                if found { Some(max) } else { None }
215            }
216        }
217    };
218}
219
220/// `AltRealData` for any container that derefs to `[f64]`.
221///
222/// Distinguishes R's `NA_real_` (a specific NaN bit pattern) from a regular
223/// IEEE NaN: `NA_real_` propagates as NA, regular NaN propagates as NaN.
224macro_rules! impl_altreal_slice {
225    ($ty:ty) => {
226        impl AltRealData for $ty {
227            fn elt(&self, i: usize) -> f64 {
228                self[i]
229            }
230
231            fn as_slice(&self) -> Option<&[f64]> {
232                let s: &[f64] = self;
233                Some(s)
234            }
235
236            fn get_region(&self, start: usize, len: usize, buf: &mut [f64]) -> usize {
237                let s: &[f64] = self;
238                let end = (start + len).min(s.len());
239                let actual_len = end.saturating_sub(start);
240                if actual_len > 0 {
241                    buf[..actual_len].copy_from_slice(&s[start..end]);
242                }
243                actual_len
244            }
245
246            fn no_na(&self) -> Option<bool> {
247                let s: &[f64] = self;
248                Some(!s.iter().any(|x| x.to_bits() == NA_REAL.to_bits()))
249            }
250
251            fn sum(&self, na_rm: bool) -> Option<f64> {
252                let s: &[f64] = self;
253                let mut sum = 0.0;
254                for &x in s.iter() {
255                    if x.to_bits() == NA_REAL.to_bits() {
256                        if !na_rm {
257                            return Some(NA_REAL);
258                        }
259                    } else if x.is_nan() {
260                        if !na_rm {
261                            return Some(f64::NAN);
262                        }
263                    } else {
264                        sum += x;
265                    }
266                }
267                Some(sum)
268            }
269
270            fn min(&self, na_rm: bool) -> Option<f64> {
271                let s: &[f64] = self;
272                let mut min = f64::INFINITY;
273                let mut found = false;
274                for &x in s.iter() {
275                    if x.to_bits() == NA_REAL.to_bits() {
276                        if !na_rm {
277                            return Some(NA_REAL);
278                        }
279                    } else if x.is_nan() {
280                        if !na_rm {
281                            return Some(f64::NAN);
282                        }
283                    } else {
284                        found = true;
285                        min = min.min(x);
286                    }
287                }
288                if found { Some(min) } else { None }
289            }
290
291            fn max(&self, na_rm: bool) -> Option<f64> {
292                let s: &[f64] = self;
293                let mut max = f64::NEG_INFINITY;
294                let mut found = false;
295                for &x in s.iter() {
296                    if x.to_bits() == NA_REAL.to_bits() {
297                        if !na_rm {
298                            return Some(NA_REAL);
299                        }
300                    } else if x.is_nan() {
301                        if !na_rm {
302                            return Some(f64::NAN);
303                        }
304                    } else {
305                        found = true;
306                        max = max.max(x);
307                    }
308                }
309                if found { Some(max) } else { None }
310            }
311        }
312    };
313}
314
315/// `AltRawData` for any container that derefs to `[u8]`. Raw has no NA.
316macro_rules! impl_altraw_slice {
317    ($ty:ty) => {
318        impl AltRawData for $ty {
319            fn elt(&self, i: usize) -> u8 {
320                self[i]
321            }
322
323            fn as_slice(&self) -> Option<&[u8]> {
324                let s: &[u8] = self;
325                Some(s)
326            }
327
328            fn get_region(&self, start: usize, len: usize, buf: &mut [u8]) -> usize {
329                let s: &[u8] = self;
330                let end = (start + len).min(s.len());
331                let actual_len = end.saturating_sub(start);
332                if actual_len > 0 {
333                    buf[..actual_len].copy_from_slice(&s[start..end]);
334                }
335                actual_len
336            }
337        }
338    };
339}
340
341/// `AltComplexData` for any container that derefs to `[Rcomplex]`.
342macro_rules! impl_altcomplex_slice {
343    ($ty:ty) => {
344        impl AltComplexData for $ty {
345            fn elt(&self, i: usize) -> Rcomplex {
346                self[i]
347            }
348
349            fn as_slice(&self) -> Option<&[Rcomplex]> {
350                let s: &[Rcomplex] = self;
351                Some(s)
352            }
353
354            fn get_region(&self, start: usize, len: usize, buf: &mut [Rcomplex]) -> usize {
355                let s: &[Rcomplex] = self;
356                let end = (start + len).min(s.len());
357                let actual_len = end.saturating_sub(start);
358                if actual_len > 0 {
359                    buf[..actual_len].copy_from_slice(&s[start..end]);
360                }
361                actual_len
362            }
363        }
364    };
365}
366
367/// `AltLogicalData` for any container that derefs to `[bool]`. `bool` can't be NA.
368macro_rules! impl_altlogical_slice {
369    ($ty:ty) => {
370        impl AltLogicalData for $ty {
371            fn elt(&self, i: usize) -> Logical {
372                self[i].into()
373            }
374
375            fn no_na(&self) -> Option<bool> {
376                Some(true) // bool can't be NA
377            }
378
379            fn sum(&self, _na_rm: bool) -> Option<i64> {
380                let s: &[bool] = self;
381                Some(s.iter().filter(|&&x| x).count() as i64)
382            }
383        }
384    };
385}
386// endregion
387
388// region: AltrepSerialize implementations for Vec<T>
389
390impl_serialize!(Vec<i32>);
391impl_serialize!(Vec<f64>);
392impl_serialize!(Vec<u8>);
393impl_serialize!(Vec<bool>);
394impl_serialize!(Vec<String>);
395impl_serialize!(Vec<Option<String>>);
396impl_serialize!(Vec<Rcomplex>);
397// Cow string vectors: serialize hits R's CHARSXP cache (no string data copy),
398// unserialize borrows via charsxp_to_cow (zero-copy for UTF-8).
399impl_serialize!(Vec<std::borrow::Cow<'static, str>>);
400impl_serialize!(Vec<Option<std::borrow::Cow<'static, str>>>);
401// endregion
402
403// region: AltrepSerialize implementations for Box<[T]>
404
405// Box<[T]> types don't have direct TryFromSexp implementations, so we manually
406// implement serialization by converting to Vec and back.
407
408impl AltrepSerialize for Box<[i32]> {
409    fn serialized_state(&self) -> SEXP {
410        use crate::into_r::IntoR;
411        self.to_vec().into_sexp()
412    }
413
414    fn unserialize(state: SEXP) -> Option<Self> {
415        use crate::from_r::TryFromSexp;
416        Vec::<i32>::try_from_sexp(state)
417            .ok()
418            .map(|v| v.into_boxed_slice())
419    }
420}
421
422impl AltrepSerialize for Box<[f64]> {
423    fn serialized_state(&self) -> SEXP {
424        use crate::into_r::IntoR;
425        self.to_vec().into_sexp()
426    }
427
428    fn unserialize(state: SEXP) -> Option<Self> {
429        use crate::from_r::TryFromSexp;
430        Vec::<f64>::try_from_sexp(state)
431            .ok()
432            .map(|v| v.into_boxed_slice())
433    }
434}
435
436impl AltrepSerialize for Box<[u8]> {
437    fn serialized_state(&self) -> SEXP {
438        use crate::into_r::IntoR;
439        self.to_vec().into_sexp()
440    }
441
442    fn unserialize(state: SEXP) -> Option<Self> {
443        use crate::from_r::TryFromSexp;
444        Vec::<u8>::try_from_sexp(state)
445            .ok()
446            .map(|v| v.into_boxed_slice())
447    }
448}
449
450impl AltrepSerialize for Box<[bool]> {
451    fn serialized_state(&self) -> SEXP {
452        use crate::into_r::IntoR;
453        self.to_vec().into_sexp()
454    }
455
456    fn unserialize(state: SEXP) -> Option<Self> {
457        use crate::from_r::TryFromSexp;
458        Vec::<bool>::try_from_sexp(state)
459            .ok()
460            .map(|v| v.into_boxed_slice())
461    }
462}
463
464impl AltrepSerialize for Box<[String]> {
465    fn serialized_state(&self) -> SEXP {
466        use crate::into_r::IntoR;
467        self.to_vec().into_sexp()
468    }
469
470    fn unserialize(state: SEXP) -> Option<Self> {
471        use crate::from_r::TryFromSexp;
472        Vec::<String>::try_from_sexp(state)
473            .ok()
474            .map(|v| v.into_boxed_slice())
475    }
476}
477
478impl AltrepSerialize for Box<[Rcomplex]> {
479    fn serialized_state(&self) -> SEXP {
480        use crate::into_r::IntoR;
481        self.to_vec().into_sexp()
482    }
483
484    fn unserialize(state: SEXP) -> Option<Self> {
485        use crate::from_r::TryFromSexp;
486        Vec::<Rcomplex>::try_from_sexp(state)
487            .ok()
488            .map(|v| v.into_boxed_slice())
489    }
490}
491// endregion
492
493// region: Built-in implementations for Vec<T>
494
495impl_len_vec!(i32);
496impl_altinteger_slice!(Vec<i32>);
497impl_dataptr_vec!(i32);
498
499impl_len_vec!(f64);
500impl_altreal_slice!(Vec<f64>);
501impl_dataptr_vec!(f64);
502
503impl_len_vec!(u8);
504impl_altraw_slice!(Vec<u8>);
505impl_dataptr_vec!(u8);
506
507impl_len_vec!(String);
508
509impl AltStringData for Vec<String> {
510    fn elt(&self, i: usize) -> Option<&str> {
511        Some(self[i].as_str())
512    }
513
514    fn no_na(&self) -> Option<bool> {
515        Some(true) // String vectors don't have NA
516    }
517}
518
519impl_len_vec!(Option<String>);
520
521impl AltStringData for Vec<Option<String>> {
522    fn elt(&self, i: usize) -> Option<&str> {
523        self[i].as_deref()
524    }
525
526    fn no_na(&self) -> Option<bool> {
527        Some(!self.iter().any(|x| x.is_none()))
528    }
529}
530
531impl AltrepLen for Vec<std::borrow::Cow<'static, str>> {
532    fn len(&self) -> usize {
533        self.len()
534    }
535}
536
537impl AltStringData for Vec<std::borrow::Cow<'static, str>> {
538    fn elt(&self, i: usize) -> Option<&str> {
539        Some(self[i].as_ref())
540    }
541
542    fn no_na(&self) -> Option<bool> {
543        Some(true) // Cow vectors don't have NA
544    }
545}
546
547impl AltrepLen for Vec<Option<std::borrow::Cow<'static, str>>> {
548    fn len(&self) -> usize {
549        self.len()
550    }
551}
552
553impl AltStringData for Vec<Option<std::borrow::Cow<'static, str>>> {
554    fn elt(&self, i: usize) -> Option<&str> {
555        self[i].as_deref()
556    }
557
558    fn no_na(&self) -> Option<bool> {
559        Some(!self.iter().any(|x| x.is_none()))
560    }
561}
562
563impl_len_vec!(bool);
564impl_altlogical_slice!(Vec<bool>);
565// endregion
566
567// region: Built-in implementations for Box<[T]> (owned slices)
568// Box<[T]> is a fat pointer (Sized) that wraps a DST slice.
569// Unlike Vec<T>, it has no capacity field - just ptr + len (2 words).
570// This makes it more memory-efficient for fixed-size data.
571//
572// Box<[T]> CAN be used directly with ALTREP via the proc-macro:
573// ```
574// #[miniextendr(class = "BoxedInts")]
575// pub struct BoxedIntsClass(Box<[i32]>);
576// ```
577//
578// Or use these trait implementations in custom wrapper structs.
579
580impl_len_boxed!(i32);
581impl_altinteger_slice!(Box<[i32]>);
582impl_dataptr_boxed!(i32);
583
584impl_len_boxed!(f64);
585impl_altreal_slice!(Box<[f64]>);
586impl_dataptr_boxed!(f64);
587
588impl_len_boxed!(u8);
589impl_altraw_slice!(Box<[u8]>);
590impl_dataptr_boxed!(u8);
591
592impl_len_boxed!(bool);
593impl_altlogical_slice!(Box<[bool]>);
594
595impl_len_boxed!(String);
596
597impl AltStringData for Box<[String]> {
598    fn elt(&self, i: usize) -> Option<&str> {
599        Some(self[i].as_str())
600    }
601
602    fn no_na(&self) -> Option<bool> {
603        Some(true) // String can't be NA
604    }
605}
606// endregion
607
608// region: AltrepSerialize implementations for Range types
609// Ranges serialize to a 2-element integer/real vector [start, end].
610
611impl AltrepSerialize for Range<i32> {
612    fn serialized_state(&self) -> SEXP {
613        use crate::into_r::IntoR;
614        vec![self.start, self.end].into_sexp()
615    }
616
617    fn unserialize(state: SEXP) -> Option<Self> {
618        use crate::from_r::TryFromSexp;
619        let v = Vec::<i32>::try_from_sexp(state).ok()?;
620        if v.len() == 2 { Some(v[0]..v[1]) } else { None }
621    }
622}
623
624impl AltrepSerialize for Range<i64> {
625    fn serialized_state(&self) -> SEXP {
626        use crate::into_r::IntoR;
627        // Store i64 bit patterns in f64 slots for lossless round-trip.
628        // Plain `as f64` loses precision for values > 2^53.
629        vec![
630            f64::from_bits(self.start as u64),
631            f64::from_bits(self.end as u64),
632        ]
633        .into_sexp()
634    }
635
636    fn unserialize(state: SEXP) -> Option<Self> {
637        use crate::from_r::TryFromSexp;
638        let v = Vec::<f64>::try_from_sexp(state).ok()?;
639        if v.len() == 2 {
640            Some((v[0].to_bits() as i64)..(v[1].to_bits() as i64))
641        } else {
642            None
643        }
644    }
645}
646
647impl AltrepSerialize for Range<f64> {
648    fn serialized_state(&self) -> SEXP {
649        use crate::into_r::IntoR;
650        vec![self.start, self.end].into_sexp()
651    }
652
653    fn unserialize(state: SEXP) -> Option<Self> {
654        use crate::from_r::TryFromSexp;
655        let v = Vec::<f64>::try_from_sexp(state).ok()?;
656        if v.len() == 2 { Some(v[0]..v[1]) } else { None }
657    }
658}
659// endregion
660
661// region: Built-in implementations for Range types
662
663impl AltrepLen for Range<i32> {
664    fn len(&self) -> usize {
665        if self.end > self.start {
666            (self.end - self.start) as usize
667        } else {
668            0
669        }
670    }
671}
672
673impl AltIntegerData for Range<i32> {
674    fn elt(&self, i: usize) -> i32 {
675        self.start + i as i32
676    }
677
678    fn is_sorted(&self) -> Option<Sortedness> {
679        Some(Sortedness::Increasing)
680    }
681
682    fn no_na(&self) -> Option<bool> {
683        // i32::MIN is NA_INTEGER in R. Check if the range contains it.
684        // Range is [start, end), so i32::MIN is included iff start <= i32::MIN < end
685        // Since start is the smallest value, we just check if start == i32::MIN
686        let contains_na = self.start == i32::MIN && self.end > i32::MIN;
687        Some(!contains_na)
688    }
689
690    fn sum(&self, na_rm: bool) -> Option<i64> {
691        let n = AltrepLen::len(self) as i64;
692        if n == 0 {
693            return Some(0);
694        }
695
696        // Check if range contains NA (i32::MIN)
697        let contains_na = self.start == i32::MIN && self.end > i32::MIN;
698        if contains_na && !na_rm {
699            return None; // NA propagates
700        }
701
702        // Sum of arithmetic sequence: n/2 * (first + last)
703        // If na_rm and contains NA, exclude the first element (i32::MIN)
704        if contains_na {
705            // Exclude first element (NA), sum from start+1 to end-1
706            let n_valid = n - 1;
707            if n_valid == 0 {
708                return Some(0);
709            }
710            let first = (self.start + 1) as i64;
711            let last = (self.end - 1) as i64;
712            Some(n_valid * (first + last) / 2)
713        } else {
714            let first = self.start as i64;
715            let last = (self.end - 1) as i64;
716            Some(n * (first + last) / 2)
717        }
718    }
719
720    fn min(&self, na_rm: bool) -> Option<i32> {
721        if AltrepLen::len(self) == 0 {
722            return None;
723        }
724
725        // Check if first element is NA
726        if self.start == i32::MIN {
727            if na_rm {
728                // Skip NA, return second element if it exists
729                if self.end > self.start + 1 {
730                    Some(self.start + 1)
731                } else {
732                    None // Only element was NA
733                }
734            } else {
735                None // NA propagates
736            }
737        } else {
738            Some(self.start)
739        }
740    }
741
742    fn max(&self, na_rm: bool) -> Option<i32> {
743        if AltrepLen::len(self) == 0 {
744            return None;
745        }
746
747        // For increasing range, max is end-1 (last element)
748        // Check if range contains NA (first element)
749        let contains_na = self.start == i32::MIN && self.end > i32::MIN;
750        if contains_na && !na_rm {
751            return None; // NA propagates
752        }
753
754        // Max is always end-1 (last element), which is not NA
755        // (NA would only be first element if start == i32::MIN)
756        Some(self.end - 1)
757    }
758}
759
760impl AltrepLen for Range<i64> {
761    fn len(&self) -> usize {
762        if self.end > self.start {
763            (self.end - self.start) as usize
764        } else {
765            0
766        }
767    }
768}
769
770impl AltIntegerData for Range<i64> {
771    fn elt(&self, i: usize) -> i32 {
772        let val = self.start.saturating_add(i as i64);
773        // Bounds check: return NA_INTEGER for values outside i32 range
774        // Also, i32::MIN is the NA sentinel, so values equal to it are NA
775        if val > i32::MAX as i64 || val <= i32::MIN as i64 {
776            crate::altrep_traits::NA_INTEGER
777        } else {
778            val as i32
779        }
780    }
781
782    fn is_sorted(&self) -> Option<Sortedness> {
783        Some(Sortedness::Increasing)
784    }
785
786    fn no_na(&self) -> Option<bool> {
787        // An element is NA if:
788        // 1. It's outside valid i32 range (< i32::MIN or > i32::MAX)
789        // 2. It equals i32::MIN (NA sentinel)
790        //
791        // For increasing range [start, end), elements range from start to end-1.
792        // Range contains NA if:
793        // - start <= i32::MIN as i64 (NA sentinel could be in range)
794        // - OR end > i32::MAX as i64 + 1 (values exceed i32::MAX)
795        // - OR start < i32::MIN as i64 (values below i32 range)
796        let na_sentinel = i32::MIN as i64;
797        let i32_max = i32::MAX as i64;
798
799        // Check if NA sentinel is in [start, end)
800        let contains_na_sentinel = self.start <= na_sentinel && self.end > na_sentinel;
801
802        // Check if any values are outside valid i32 range
803        // Valid range for ALTREP integers: (i32::MIN, i32::MAX] (excluding NA sentinel)
804        let has_underflow = self.start < na_sentinel;
805        let has_overflow = (self.end - 1) > i32_max;
806
807        Some(!contains_na_sentinel && !has_underflow && !has_overflow)
808    }
809
810    fn sum(&self, na_rm: bool) -> Option<i64> {
811        let n = AltrepLen::len(self) as i64;
812        if n == 0 {
813            return Some(0);
814        }
815
816        // Check if range contains any NA values
817        let na_sentinel = i32::MIN as i64;
818        let i32_max = i32::MAX as i64;
819        let contains_na_sentinel = self.start <= na_sentinel && self.end > na_sentinel;
820        let has_underflow = self.start < na_sentinel;
821        let has_overflow = (self.end - 1) > i32_max;
822        let has_na = contains_na_sentinel || has_underflow || has_overflow;
823
824        if has_na && !na_rm {
825            return None; // NA propagates
826        }
827
828        if has_na {
829            // When na_rm=true, we need to exclude NA values
830            // This is complex for ranges with out-of-bounds values, so let R compute
831            return None;
832        }
833
834        let first = self.start;
835        let last = self.end - 1;
836
837        // Use checked arithmetic to detect overflow
838        // Formula: n * (first + last) / 2
839        let sum_endpoints = first.checked_add(last)?;
840        let product = n.checked_mul(sum_endpoints)?;
841        Some(product / 2)
842    }
843
844    fn min(&self, na_rm: bool) -> Option<i32> {
845        if AltrepLen::len(self) == 0 {
846            return None;
847        }
848
849        let na_sentinel = i32::MIN as i64;
850        let i32_max = i32::MAX as i64;
851
852        // Check for NA conditions
853        let contains_na_sentinel = self.start <= na_sentinel && self.end > na_sentinel;
854        let has_underflow = self.start < na_sentinel;
855        let has_overflow = (self.end - 1) > i32_max;
856        let has_na = contains_na_sentinel || has_underflow || has_overflow;
857
858        if has_na && !na_rm {
859            return None; // NA propagates
860        }
861
862        if has_na {
863            // Complex case: need to find first non-NA value
864            // Let R compute this
865            return None;
866        }
867
868        // No NA, return start (which is within valid i32 range)
869        Some(self.start as i32)
870    }
871
872    fn max(&self, na_rm: bool) -> Option<i32> {
873        if AltrepLen::len(self) == 0 {
874            return None;
875        }
876
877        let na_sentinel = i32::MIN as i64;
878        let i32_max = i32::MAX as i64;
879
880        // Check for NA conditions
881        let contains_na_sentinel = self.start <= na_sentinel && self.end > na_sentinel;
882        let has_underflow = self.start < na_sentinel;
883        let has_overflow = (self.end - 1) > i32_max;
884        let has_na = contains_na_sentinel || has_underflow || has_overflow;
885
886        if has_na && !na_rm {
887            return None; // NA propagates
888        }
889
890        if has_na {
891            // Complex case: need to find last non-NA value
892            // Let R compute this
893            return None;
894        }
895
896        // No NA, return end-1 (which is within valid i32 range)
897        Some((self.end - 1) as i32)
898    }
899}
900
901impl AltrepLen for Range<f64> {
902    fn len(&self) -> usize {
903        // For f64 ranges, assume step of 1.0
904        if self.end > self.start {
905            (self.end - self.start).ceil() as usize
906        } else {
907            0
908        }
909    }
910}
911
912impl AltRealData for Range<f64> {
913    fn elt(&self, i: usize) -> f64 {
914        self.start + i as f64
915    }
916
917    fn is_sorted(&self) -> Option<Sortedness> {
918        Some(Sortedness::Increasing)
919    }
920
921    fn no_na(&self) -> Option<bool> {
922        Some(true)
923    }
924
925    fn sum(&self, _na_rm: bool) -> Option<f64> {
926        let n = AltrepLen::len(self) as f64;
927        if n == 0.0 {
928            return Some(0.0);
929        }
930        let first = self.start;
931        let last = self.start + (n - 1.0);
932        Some(n * (first + last) / 2.0)
933    }
934
935    fn min(&self, _na_rm: bool) -> Option<f64> {
936        if AltrepLen::len(self) > 0 {
937            Some(self.start)
938        } else {
939            None
940        }
941    }
942
943    fn max(&self, _na_rm: bool) -> Option<f64> {
944        if AltrepLen::len(self) > 0 {
945            Some(self.start + (AltrepLen::len(self) - 1) as f64)
946        } else {
947            None
948        }
949    }
950}
951// endregion
952
953// region: Built-in implementations for slices (read-only)
954
955impl_len_slice!(i32);
956impl_altinteger_slice!(&[i32]);
957
958impl_len_slice!(f64);
959impl_altreal_slice!(&[f64]);
960
961impl_len_slice!(u8);
962impl_altraw_slice!(&[u8]);
963
964impl_len_slice!(bool);
965impl_altlogical_slice!(&[bool]);
966
967impl_len_slice!(String);
968
969impl AltStringData for &[String] {
970    fn elt(&self, i: usize) -> Option<&str> {
971        Some(self[i].as_str())
972    }
973}
974
975impl_len_slice!(&str);
976
977impl AltStringData for &[&str] {
978    fn elt(&self, i: usize) -> Option<&str> {
979        Some(self[i])
980    }
981}
982// endregion
983
984// region: NOTE on &'static [T] (static slices)
985//
986// `&'static [T]` is Sized (fat pointer: ptr + len) and satisfies 'static,
987// so it can be used DIRECTLY with ALTREP via ExternalPtr.
988//
989// The data trait implementations above for `&[T]` already cover `&'static [T]`
990// since `&'static [T]` is a subtype of `&[T]`. The ALTREP trait implementations
991// (Altrep, AltVec, AltInteger, etc.) are provided separately in altrep_impl.rs.
992//
993// Use cases:
994// - Const arrays: `static DATA: [i32; 5] = [1, 2, 3, 4, 5]; create_altrep(&DATA[..])`
995// - Leaked data: `let s: &'static [i32] = Box::leak(vec.into_boxed_slice());`
996// - Memory-mapped files with 'static lifetime
997// endregion
998
999// region: Built-in implementations for arrays (owned, fixed-size)
1000
1001impl_len_array!(i32);
1002
1003impl<const N: usize> AltIntegerData for [i32; N] {
1004    fn elt(&self, i: usize) -> i32 {
1005        self[i]
1006    }
1007
1008    fn as_slice(&self) -> Option<&[i32]> {
1009        Some(self.as_slice())
1010    }
1011
1012    fn get_region(&self, start: usize, len: usize, buf: &mut [i32]) -> usize {
1013        let end = (start + len).min(N);
1014        let actual_len = end.saturating_sub(start);
1015        if actual_len > 0 {
1016            buf[..actual_len].copy_from_slice(&self[start..end]);
1017        }
1018        actual_len
1019    }
1020
1021    fn no_na(&self) -> Option<bool> {
1022        Some(!self.contains(&i32::MIN))
1023    }
1024}
1025
1026impl_len_array!(f64);
1027
1028impl<const N: usize> AltRealData for [f64; N] {
1029    fn elt(&self, i: usize) -> f64 {
1030        self[i]
1031    }
1032
1033    fn as_slice(&self) -> Option<&[f64]> {
1034        Some(self.as_slice())
1035    }
1036
1037    fn get_region(&self, start: usize, len: usize, buf: &mut [f64]) -> usize {
1038        let end = (start + len).min(N);
1039        let actual_len = end.saturating_sub(start);
1040        if actual_len > 0 {
1041            buf[..actual_len].copy_from_slice(&self[start..end]);
1042        }
1043        actual_len
1044    }
1045
1046    fn no_na(&self) -> Option<bool> {
1047        Some(!self.iter().any(|x| x.to_bits() == NA_REAL.to_bits()))
1048    }
1049}
1050
1051impl_len_array!(bool);
1052
1053impl<const N: usize> AltLogicalData for [bool; N] {
1054    fn elt(&self, i: usize) -> Logical {
1055        Logical::from_bool(self[i])
1056    }
1057
1058    fn no_na(&self) -> Option<bool> {
1059        Some(true) // bool arrays can't have NA
1060    }
1061}
1062
1063impl_len_array!(u8);
1064
1065impl<const N: usize> AltRawData for [u8; N] {
1066    fn elt(&self, i: usize) -> u8 {
1067        self[i]
1068    }
1069
1070    fn as_slice(&self) -> Option<&[u8]> {
1071        Some(self.as_slice())
1072    }
1073
1074    fn get_region(&self, start: usize, len: usize, buf: &mut [u8]) -> usize {
1075        let end = (start + len).min(N);
1076        let actual_len = end.saturating_sub(start);
1077        if actual_len > 0 {
1078            buf[..actual_len].copy_from_slice(&self[start..end]);
1079        }
1080        actual_len
1081    }
1082}
1083
1084impl_len_array!(String);
1085
1086impl<const N: usize> AltStringData for [String; N] {
1087    fn elt(&self, i: usize) -> Option<&str> {
1088        Some(self[i].as_str())
1089    }
1090}
1091// endregion
1092
1093// region: Built-in implementations for Vec<Rcomplex> (complex numbers)
1094
1095impl_len_vec!(Rcomplex);
1096impl_altcomplex_slice!(Vec<Rcomplex>);
1097impl_dataptr_vec!(Rcomplex);
1098// endregion
1099
1100// region: Built-in implementations for Box<[Rcomplex]>
1101
1102impl_len_boxed!(Rcomplex);
1103impl_altcomplex_slice!(Box<[Rcomplex]>);
1104impl_dataptr_boxed!(Rcomplex);
1105// endregion
1106
1107// region: Built-in implementations for [Rcomplex; N] (complex arrays)
1108
1109impl_len_array!(Rcomplex);
1110
1111impl<const N: usize> AltComplexData for [Rcomplex; N] {
1112    fn elt(&self, i: usize) -> Rcomplex {
1113        self[i]
1114    }
1115
1116    fn as_slice(&self) -> Option<&[Rcomplex]> {
1117        Some(self.as_slice())
1118    }
1119
1120    fn get_region(&self, start: usize, len: usize, buf: &mut [Rcomplex]) -> usize {
1121        let end = (start + len).min(N);
1122        let actual_len = end.saturating_sub(start);
1123        if actual_len > 0 {
1124            buf[..actual_len].copy_from_slice(&self[start..end]);
1125        }
1126        actual_len
1127    }
1128}
1129// endregion
1130
1131// region: Built-in implementations for Cow<'static, [T]>
1132//
1133// Cow<'static, [T]> enables zero-copy borrows from R vectors while retaining
1134// ownership semantics. Borrowed variants expose the underlying R data directly
1135// via dataptr; Owned variants behave like Vec<T>. Copy-on-write happens
1136// automatically when R requests a writable dataptr on a Borrowed variant.
1137
1138macro_rules! impl_len_cow {
1139    ($elem:ty) => {
1140        impl AltrepLen for Cow<'static, [$elem]> {
1141            fn len(&self) -> usize {
1142                <[$elem]>::len(self)
1143            }
1144        }
1145    };
1146}
1147
1148macro_rules! impl_dataptr_cow {
1149    ($elem:ty) => {
1150        impl AltrepDataptr<$elem> for Cow<'static, [$elem]> {
1151            fn dataptr(&mut self, writable: bool) -> Option<*mut $elem> {
1152                if writable {
1153                    // to_mut() triggers copy-on-write for Borrowed variants.
1154                    // Only do this when R intends to write through the pointer.
1155                    Some(self.to_mut().as_mut_ptr())
1156                } else {
1157                    // Read-only access: return the existing pointer without
1158                    // forcing a copy for Borrowed variants.
1159                    Some(self.as_ptr().cast_mut())
1160                }
1161            }
1162
1163            fn dataptr_or_null(&self) -> Option<*const $elem> {
1164                Some(self.as_ptr())
1165            }
1166        }
1167    };
1168}
1169
1170macro_rules! impl_serialize_cow {
1171    ($elem:ty) => {
1172        impl AltrepSerialize for Cow<'static, [$elem]> {
1173            fn serialized_state(&self) -> SEXP {
1174                use crate::into_r::IntoR;
1175                self.clone().into_sexp()
1176            }
1177
1178            fn unserialize(state: SEXP) -> Option<Self> {
1179                use crate::from_r::TryFromSexp;
1180                Cow::<'static, [$elem]>::try_from_sexp(state).ok()
1181            }
1182        }
1183    };
1184}
1185
1186impl_len_cow!(i32);
1187impl_altinteger_slice!(Cow<'static, [i32]>);
1188impl_dataptr_cow!(i32);
1189impl_serialize_cow!(i32);
1190
1191impl_len_cow!(f64);
1192impl_altreal_slice!(Cow<'static, [f64]>);
1193impl_dataptr_cow!(f64);
1194impl_serialize_cow!(f64);
1195
1196impl_len_cow!(u8);
1197impl_altraw_slice!(Cow<'static, [u8]>);
1198impl_dataptr_cow!(u8);
1199impl_serialize_cow!(u8);
1200
1201impl_len_cow!(Rcomplex);
1202impl_altcomplex_slice!(Cow<'static, [Rcomplex]>);
1203impl_dataptr_cow!(Rcomplex);
1204impl_serialize_cow!(Rcomplex);
1205// endregion
1206
1207// region: Low-level ALTREP trait implementations
1208//
1209// The low-level trait impls (Altrep, AltVec, Alt*, InferBase) for builtin types
1210// are located in altrep_impl.rs. This is because the impl_alt*_from_data! macros
1211// are defined there and need to be in the same module.
1212//
1213// See altrep_impl.rs for:
1214// - Vec<i32>, Vec<f64>, Vec<bool>, Vec<u8>, Vec<String>, Vec<Rcomplex>
1215// - Box<[i32]>, Box<[f64]>, Box<[bool]>, Box<[u8]>, Box<[String]>, Box<[Rcomplex]>
1216// - Cow<'static, [i32]>, Cow<'static, [f64]>, Cow<'static, [u8]>, Cow<'static, [Rcomplex]>
1217// - Range<i32>, Range<i64>, Range<f64>
1218// endregion
1219
1220#[cfg(test)]
1221mod tests {
1222    use super::*;
1223    use crate::altrep_data::AltRealData;
1224
1225    // region: NA_REAL bit pattern tests
1226
1227    /// Verify NA_REAL has the expected R bit pattern.
1228    #[test]
1229    fn na_real_bit_pattern() {
1230        assert_eq!(NA_REAL.to_bits(), 0x7FF0_0000_0000_07A2);
1231    }
1232
1233    /// Regular NaN is NOT the same bit pattern as NA_real_.
1234    #[test]
1235    fn nan_is_not_na_real() {
1236        let nan = f64::NAN;
1237        assert!(nan.is_nan());
1238        assert_ne!(nan.to_bits(), NA_REAL.to_bits());
1239    }
1240
1241    // endregion
1242
1243    // region: Vec<f64> no_na — NA vs NaN
1244
1245    /// A vector with only regular (non-NA) NaN should report no_na = Some(true).
1246    /// Regular NaN is a valid floating-point value, not R's NA.
1247    #[test]
1248    fn vec_f64_no_na_with_regular_nan_is_true() {
1249        let v: Vec<f64> = vec![1.0, f64::NAN, 3.0];
1250        assert_eq!(AltRealData::no_na(&v), Some(true));
1251    }
1252
1253    /// A vector containing NA_real_ should report no_na = Some(false).
1254    #[test]
1255    fn vec_f64_no_na_with_na_real_is_false() {
1256        let v: Vec<f64> = vec![1.0, NA_REAL, 3.0];
1257        assert_eq!(AltRealData::no_na(&v), Some(false));
1258    }
1259
1260    /// A vector with no NaN and no NA should report no_na = Some(true).
1261    #[test]
1262    fn vec_f64_no_na_all_finite_is_true() {
1263        let v: Vec<f64> = vec![1.0, 2.0, 3.0];
1264        assert_eq!(AltRealData::no_na(&v), Some(true));
1265    }
1266
1267    // endregion
1268
1269    // region: Vec<f64> sum — NA vs NaN
1270
1271    /// sum with na_rm=false and NA_real_ present should return Some(NA_REAL).
1272    #[test]
1273    fn vec_f64_sum_na_propagates() {
1274        let v: Vec<f64> = vec![1.0, NA_REAL, 3.0];
1275        let result = AltRealData::sum(&v, false);
1276        assert!(result.is_some());
1277        let bits = result.unwrap().to_bits();
1278        assert_eq!(
1279            bits,
1280            NA_REAL.to_bits(),
1281            "sum with NA should return NA_real_"
1282        );
1283    }
1284
1285    /// sum with na_rm=true and NA_real_ present should skip the NA and sum the rest.
1286    #[test]
1287    fn vec_f64_sum_na_rm_skips_na() {
1288        let v: Vec<f64> = vec![1.0, NA_REAL, 3.0];
1289        let result = AltRealData::sum(&v, true);
1290        assert_eq!(result, Some(4.0));
1291    }
1292
1293    /// sum with na_rm=false and regular NaN present should return Some(NaN) — NOT NA.
1294    #[test]
1295    fn vec_f64_sum_nan_propagates_as_nan_not_na() {
1296        let v: Vec<f64> = vec![1.0, f64::NAN, 3.0];
1297        let result = AltRealData::sum(&v, false);
1298        assert!(result.is_some());
1299        let val = result.unwrap();
1300        assert!(val.is_nan(), "sum with NaN (not NA) should return NaN");
1301        assert_ne!(
1302            val.to_bits(),
1303            NA_REAL.to_bits(),
1304            "sum with regular NaN should NOT return NA_real_"
1305        );
1306    }
1307
1308    /// sum with na_rm=true and regular NaN present should skip the NaN.
1309    #[test]
1310    fn vec_f64_sum_nan_rm_skips_nan() {
1311        let v: Vec<f64> = vec![1.0, f64::NAN, 3.0];
1312        let result = AltRealData::sum(&v, true);
1313        assert_eq!(result, Some(4.0));
1314    }
1315
1316    // endregion
1317
1318    // region: Box<[f64]> no_na — NA vs NaN
1319
1320    #[test]
1321    fn box_f64_no_na_with_regular_nan_is_true() {
1322        let v: Box<[f64]> = vec![1.0, f64::NAN, 3.0].into_boxed_slice();
1323        assert_eq!(AltRealData::no_na(&v), Some(true));
1324    }
1325
1326    #[test]
1327    fn box_f64_no_na_with_na_real_is_false() {
1328        let v: Box<[f64]> = vec![1.0, NA_REAL, 3.0].into_boxed_slice();
1329        assert_eq!(AltRealData::no_na(&v), Some(false));
1330    }
1331
1332    // endregion
1333
1334    // region: &[f64] no_na — NA vs NaN
1335
1336    #[test]
1337    fn slice_f64_no_na_with_regular_nan_is_true() {
1338        let data: &[f64] = &[1.0, f64::NAN, 3.0];
1339        assert_eq!(AltRealData::no_na(&data), Some(true));
1340    }
1341
1342    #[test]
1343    fn slice_f64_no_na_with_na_real_is_false() {
1344        let data: &[f64] = &[1.0, NA_REAL, 3.0];
1345        assert_eq!(AltRealData::no_na(&data), Some(false));
1346    }
1347
1348    // endregion
1349
1350    // region: [f64; N] no_na — NA vs NaN
1351
1352    #[test]
1353    fn array_f64_no_na_with_regular_nan_is_true() {
1354        let arr: [f64; 3] = [1.0, f64::NAN, 3.0];
1355        assert_eq!(AltRealData::no_na(&arr), Some(true));
1356    }
1357
1358    #[test]
1359    fn array_f64_no_na_with_na_real_is_false() {
1360        let arr: [f64; 3] = [1.0, NA_REAL, 3.0];
1361        assert_eq!(AltRealData::no_na(&arr), Some(false));
1362    }
1363
1364    // endregion
1365
1366    // region: Cow<'static, [f64]> no_na — NA vs NaN
1367
1368    #[test]
1369    fn cow_f64_no_na_with_regular_nan_is_true() {
1370        let v: Cow<'static, [f64]> = Cow::Owned(vec![1.0, f64::NAN, 3.0]);
1371        assert_eq!(AltRealData::no_na(&v), Some(true));
1372    }
1373
1374    #[test]
1375    fn cow_f64_no_na_with_na_real_is_false() {
1376        let v: Cow<'static, [f64]> = Cow::Owned(vec![1.0, NA_REAL, 3.0]);
1377        assert_eq!(AltRealData::no_na(&v), Some(false));
1378    }
1379
1380    // endregion
1381}