Skip to main content

miniextendr_api/altrep_data/
deque.rs

1//! ALTREP storage for native-element ring buffers.
2//!
3//! Element and region reads preserve the split buffer. A contiguous pointer is
4//! exposed only after `VecDeque::make_contiguous`, without converting the owner
5//! to a Vec or copying into an R vector.
6
7use std::collections::VecDeque;
8
9use super::{
10    AltComplexData, AltIntegerData, AltRawData, AltRealData, AltrepDataptr, AltrepLen,
11    AltrepSerialize,
12};
13use crate::{IntoR, Rcomplex, SEXP, TryFromSexp};
14
15impl<T> AltrepLen for VecDeque<T> {
16    fn altrep_len(&self) -> usize {
17        VecDeque::len(self)
18    }
19}
20
21/// Borrow the complete logical sequence only when it occupies one segment.
22fn contiguous_slice<T>(values: &VecDeque<T>) -> Option<&[T]> {
23    let (front, back) = values.as_slices();
24    if back.is_empty() {
25        Some(front)
26    } else if front.is_empty() {
27        Some(back)
28    } else {
29        None
30    }
31}
32
33/// Copy at most two contiguous regions, with no allocation or rearrangement.
34fn copy_region<T: Copy>(values: &VecDeque<T>, start: usize, len: usize, out: &mut [T]) -> usize {
35    let n = len.min(out.len()).min(values.len().saturating_sub(start));
36    if n == 0 {
37        return 0;
38    }
39    let (front, back) = values.as_slices();
40    let first = n.min(front.len().saturating_sub(start));
41    if first != 0 {
42        out[..first].copy_from_slice(&front[start..start + first]);
43    }
44    if first != n {
45        let offset = start.saturating_sub(front.len());
46        out[first..n].copy_from_slice(&back[offset..offset + n - first]);
47    }
48    n
49}
50
51macro_rules! impl_native_deque {
52    ($elem:ty, $family:ident) => {
53        impl $family for VecDeque<$elem> {
54            fn elt(&self, i: usize) -> $elem {
55                self[i]
56            }
57
58            fn as_slice(&self) -> Option<&[$elem]> {
59                contiguous_slice(self)
60            }
61
62            fn get_region(&self, start: usize, len: usize, out: &mut [$elem]) -> usize {
63                copy_region(self, start, len, out)
64            }
65        }
66
67        impl AltrepDataptr<$elem> for VecDeque<$elem> {
68            fn dataptr(&mut self, _writable: bool) -> Option<*mut $elem> {
69                Some(self.make_contiguous().as_mut_ptr())
70            }
71
72            fn dataptr_or_null(&self) -> Option<*const $elem> {
73                contiguous_slice(self).map(<[$elem]>::as_ptr)
74            }
75        }
76
77        impl AltrepSerialize for VecDeque<$elem> {
78            fn serialized_state(&self) -> SEXP {
79                self.clone().into_sexp()
80            }
81
82            fn unserialize(state: SEXP) -> Option<Self> {
83                Self::try_from_sexp(state).ok()
84            }
85        }
86    };
87}
88
89impl AltIntegerData for VecDeque<i32> {
    fn elt(&self, i: usize) -> i32 { self[i] }
    fn as_slice(&self) -> Option<&[i32]> { contiguous_slice(self) }
    fn get_region(&self, start: usize, len: usize, out: &mut [i32]) -> usize {
        copy_region(self, start, len, out)
    }
}
impl AltrepDataptr<i32> for VecDeque<i32> {
    fn dataptr(&mut self, _writable: bool) -> Option<*mut i32> {
        Some(self.make_contiguous().as_mut_ptr())
    }
    fn dataptr_or_null(&self) -> Option<*const i32> {
        contiguous_slice(self).map(<[i32]>::as_ptr)
    }
}
impl AltrepSerialize for VecDeque<i32> {
    fn serialized_state(&self) -> SEXP { self.clone().into_sexp() }
    fn unserialize(state: SEXP) -> Option<Self> {
        Self::try_from_sexp(state).ok()
    }
}impl_native_deque!(i32, AltIntegerData);
90impl AltRealData for VecDeque<f64> {
    fn elt(&self, i: usize) -> f64 { self[i] }
    fn as_slice(&self) -> Option<&[f64]> { contiguous_slice(self) }
    fn get_region(&self, start: usize, len: usize, out: &mut [f64]) -> usize {
        copy_region(self, start, len, out)
    }
}
impl AltrepDataptr<f64> for VecDeque<f64> {
    fn dataptr(&mut self, _writable: bool) -> Option<*mut f64> {
        Some(self.make_contiguous().as_mut_ptr())
    }
    fn dataptr_or_null(&self) -> Option<*const f64> {
        contiguous_slice(self).map(<[f64]>::as_ptr)
    }
}
impl AltrepSerialize for VecDeque<f64> {
    fn serialized_state(&self) -> SEXP { self.clone().into_sexp() }
    fn unserialize(state: SEXP) -> Option<Self> {
        Self::try_from_sexp(state).ok()
    }
}impl_native_deque!(f64, AltRealData);
91impl AltRawData for VecDeque<u8> {
    fn elt(&self, i: usize) -> u8 { self[i] }
    fn as_slice(&self) -> Option<&[u8]> { contiguous_slice(self) }
    fn get_region(&self, start: usize, len: usize, out: &mut [u8]) -> usize {
        copy_region(self, start, len, out)
    }
}
impl AltrepDataptr<u8> for VecDeque<u8> {
    fn dataptr(&mut self, _writable: bool) -> Option<*mut u8> {
        Some(self.make_contiguous().as_mut_ptr())
    }
    fn dataptr_or_null(&self) -> Option<*const u8> {
        contiguous_slice(self).map(<[u8]>::as_ptr)
    }
}
impl AltrepSerialize for VecDeque<u8> {
    fn serialized_state(&self) -> SEXP { self.clone().into_sexp() }
    fn unserialize(state: SEXP) -> Option<Self> {
        Self::try_from_sexp(state).ok()
    }
}impl_native_deque!(u8, AltRawData);
92impl AltComplexData for VecDeque<Rcomplex> {
    fn elt(&self, i: usize) -> Rcomplex { self[i] }
    fn as_slice(&self) -> Option<&[Rcomplex]> { contiguous_slice(self) }
    fn get_region(&self, start: usize, len: usize, out: &mut [Rcomplex])
        -> usize {
        copy_region(self, start, len, out)
    }
}
impl AltrepDataptr<Rcomplex> for VecDeque<Rcomplex> {
    fn dataptr(&mut self, _writable: bool) -> Option<*mut Rcomplex> {
        Some(self.make_contiguous().as_mut_ptr())
    }
    fn dataptr_or_null(&self) -> Option<*const Rcomplex> {
        contiguous_slice(self).map(<[Rcomplex]>::as_ptr)
    }
}
impl AltrepSerialize for VecDeque<Rcomplex> {
    fn serialized_state(&self) -> SEXP { self.clone().into_sexp() }
    fn unserialize(state: SEXP) -> Option<Self> {
        Self::try_from_sexp(state).ok()
    }
}impl_native_deque!(Rcomplex, AltComplexData);
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn regions_cross_the_ring_boundary_without_rearranging_storage() {
100        let mut values = VecDeque::from(vec![0, 1, 2, 3, 4, 5, 6, 7]);
101        values.rotate_left(3);
102        let split = values.as_slices().0.len();
103        assert!(split > 0 && split < values.len());
104        let expected = values.iter().copied().collect::<Vec<_>>();
105        for start in 0..=values.len() + 1 {
106            for requested in [0, 1, 3, 8, usize::MAX] {
107                let mut out = [-1; 4];
108                let n = copy_region(&values, start, requested, &mut out);
109                let want = expected
110                    .iter()
111                    .skip(start)
112                    .take(requested)
113                    .take(4)
114                    .copied()
115                    .collect::<Vec<_>>();
116                assert_eq!(&out[..n], want.as_slice());
117                assert!(out[n..].iter().all(|&x| x == -1));
118                assert_eq!(values.as_slices().0.len(), split);
119            }
120        }
121        assert_eq!(copy_region(&values, usize::MAX, usize::MAX, &mut [0; 4]), 0);
122        assert_eq!(copy_region(&values, 0, 4, &mut []), 0);
123        assert_eq!(copy_region(&VecDeque::<i32>::new(), 0, 4, &mut [0; 4]), 0);
124    }
125
126    #[test]
127    fn pointer_access_makes_the_existing_owner_contiguous() {
128        let mut values = VecDeque::from(vec![1, 2, 3, 4]);
129        values.rotate_left(1);
130        assert!(AltrepDataptr::dataptr_or_null(&values).is_none());
131        let pointer = AltrepDataptr::dataptr(&mut values, true).unwrap();
132        unsafe { *pointer = 90 };
133        assert_eq!(values.iter().copied().collect::<Vec<_>>(), [90, 3, 4, 1]);
134        assert_eq!(values.as_slices().1.len(), 0);
135        assert_eq!(
136            AltrepDataptr::dataptr_or_null(&values),
137            Some(pointer.cast_const())
138        );
139    }
140}