miniextendr_api/altrep_data/
buffer.rs1use super::{AltrepDataptr, AltrepLen, AltrepSerialize};
3use crate::{IntoR, RNativeType, SEXP, TryFromSexp};
4
5#[derive(#[automatically_derived]
impl<T: ::core::fmt::Debug + RNativeType> ::core::fmt::Debug for
AltrepBuffer<T> {
#[inline]
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "AltrepBuffer",
"values", &self.values, "exposed", &self.exposed, "retired",
&&self.retired)
}
}Debug)]
22pub struct AltrepBuffer<T: RNativeType> {
23 values: Vec<T>,
24 exposed: bool,
25 retired: Vec<Vec<T>>,
26}
27
28impl<T: RNativeType> AltrepBuffer<T> {
29 pub fn new(values: Vec<T>) -> Self {
31 Self {
32 values,
33 exposed: false,
34 retired: Vec::new(),
35 }
36 }
37
38 pub fn as_slice(&self) -> &[T] {
40 &self.values
41 }
42
43 pub fn edit<R>(&mut self, edit: impl FnOnce(&mut Vec<T>) -> R) -> R {
47 if self.exposed {
48 let next = self.values.clone();
49 self.retired.push(std::mem::replace(&mut self.values, next));
50 self.exposed = false;
51 }
52 edit(&mut self.values)
53 }
54
55 pub fn len(&self) -> usize {
57 self.values.len()
58 }
59 pub fn capacity(&self) -> usize {
61 self.values.capacity()
62 }
63 pub fn is_empty(&self) -> bool {
65 self.values.is_empty()
66 }
67}
68
69impl<T: RNativeType> AsRef<[T]> for AltrepBuffer<T> {
70 fn as_ref(&self) -> &[T] {
71 self.as_slice()
72 }
73}
74
75impl<T: RNativeType> From<Vec<T>> for AltrepBuffer<T> {
76 fn from(values: Vec<T>) -> Self {
77 Self::new(values)
78 }
79}
80impl<T: RNativeType> Clone for AltrepBuffer<T> {
81 fn clone(&self) -> Self {
82 Self::new(self.values.clone())
83 }
84 fn clone_from(&mut self, source: &Self) {
85 self.edit(|values| values.clone_from(&source.values));
86 }
87}
88impl<T: RNativeType> AltrepLen for AltrepBuffer<T> {
89 fn altrep_len(&self) -> usize {
90 self.len()
91 }
92}
93impl<T: RNativeType> crate::externalptr::TypedExternal for AltrepBuffer<T> {
94 const TYPE_NAME: &'static str = "AltrepBuffer";
95 const TYPE_NAME_CSTR: &'static [u8] = b"AltrepBuffer\0";
96 const TYPE_ID_CSTR: &'static [u8] = b"miniextendr::AltrepBuffer\0";
97}
98impl<T: RNativeType> AltrepSerialize for AltrepBuffer<T>
99where
100 Vec<T>: IntoR + TryFromSexp,
101{
102 fn serialized_state(&self) -> SEXP {
103 self.values.clone().into_sexp()
104 }
105 fn unserialize(state: SEXP) -> Option<Self> {
106 Vec::<T>::try_from_sexp(state).ok().map(Self::new)
107 }
108}
109
110macro_rules! native_buffer {
111 ($element:ty, $storage:ty, $family:ident) => {
112 impl super::$family for AltrepBuffer<$element> {
113 fn elt(&self, i: usize) -> $storage {
114 self.values[i]
115 }
116 fn as_slice(&self) -> Option<&[$storage]> {
117 Some(&self.values)
118 }
119 }
120 impl AltrepDataptr<$storage> for AltrepBuffer<$element> {
121 fn dataptr(&mut self, _writable: bool) -> Option<*mut $storage> {
122 self.exposed = true;
123 Some(self.values.as_mut_ptr().cast())
124 }
125 }
128 };
129}
130impl super::AltIntegerData for AltrepBuffer<i32> {
fn elt(&self, i: usize) -> i32 { self.values[i] }
fn as_slice(&self) -> Option<&[i32]> { Some(&self.values) }
}
impl AltrepDataptr<i32> for AltrepBuffer<i32> {
fn dataptr(&mut self, _writable: bool) -> Option<*mut i32> {
self.exposed = true;
Some(self.values.as_mut_ptr().cast())
}
}native_buffer!(i32, i32, AltIntegerData);
131impl super::AltRealData for AltrepBuffer<f64> {
fn elt(&self, i: usize) -> f64 { self.values[i] }
fn as_slice(&self) -> Option<&[f64]> { Some(&self.values) }
}
impl AltrepDataptr<f64> for AltrepBuffer<f64> {
fn dataptr(&mut self, _writable: bool) -> Option<*mut f64> {
self.exposed = true;
Some(self.values.as_mut_ptr().cast())
}
}native_buffer!(f64, f64, AltRealData);
132impl super::AltRawData for AltrepBuffer<u8> {
fn elt(&self, i: usize) -> u8 { self.values[i] }
fn as_slice(&self) -> Option<&[u8]> { Some(&self.values) }
}
impl AltrepDataptr<u8> for AltrepBuffer<u8> {
fn dataptr(&mut self, _writable: bool) -> Option<*mut u8> {
self.exposed = true;
Some(self.values.as_mut_ptr().cast())
}
}native_buffer!(u8, u8, AltRawData);
133impl super::AltComplexData for AltrepBuffer<crate::Rcomplex> {
fn elt(&self, i: usize) -> crate::Rcomplex { self.values[i] }
fn as_slice(&self) -> Option<&[crate::Rcomplex]> { Some(&self.values) }
}
impl AltrepDataptr<crate::Rcomplex> for AltrepBuffer<crate::Rcomplex> {
fn dataptr(&mut self, _writable: bool) -> Option<*mut crate::Rcomplex> {
self.exposed = true;
Some(self.values.as_mut_ptr().cast())
}
}native_buffer!(crate::Rcomplex, crate::Rcomplex, AltComplexData);
134
135impl super::AltLogicalData for AltrepBuffer<crate::RLogical> {
136 fn elt(&self, i: usize) -> super::Logical {
137 self.values[i].into()
138 }
139 fn as_r_slice(&self) -> Option<&[i32]> {
140 Some(unsafe { std::slice::from_raw_parts(self.values.as_ptr().cast(), self.values.len()) })
142 }
143}
144impl AltrepDataptr<i32> for AltrepBuffer<crate::RLogical> {
145 fn dataptr(&mut self, _writable: bool) -> Option<*mut i32> {
146 self.exposed = true;
147 Some(self.values.as_mut_ptr().cast())
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn growth_replacement_and_panics_keep_exposed_allocations_alive() {
157 let mut values = AltrepBuffer::new(vec![1_i32, 2]);
158 let old = values.dataptr(true).unwrap();
159 values.edit(|v| {
160 v[0] = 3;
161 v.resize(4096, 9);
162 });
163 let current = values.dataptr(true).unwrap();
164 assert_ne!(old, current);
165 unsafe {
166 assert_eq!(*old, 1);
167 assert_eq!(*current, 3);
168 }
169 assert_eq!(values.len(), 4096);
170 let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
171 values.edit(|v| {
172 v.clear();
173 panic!("interrupted edit");
174 });
175 }));
176 assert!(result.is_err());
177 unsafe {
178 assert_eq!(*old, 1);
179 assert_eq!(*current, 3);
180 }
181 assert!(values.is_empty());
182 }
183
184 #[test]
185 fn unexposed_edits_reuse_capacity_and_clones_detach() {
186 let mut values = AltrepBuffer::new(Vec::<i32>::with_capacity(8));
187 let before = values.values.as_ptr();
188 values.edit(|v| v.extend([1, 2, 3]));
189 assert_eq!(values.values.as_ptr(), before);
190 let exposed = values.dataptr(true).unwrap();
191 let mut copy = values.clone();
192 copy.edit(|v| v[0] = 7);
193 unsafe {
194 assert_eq!(*exposed, 1);
195 }
196 assert_eq!(copy.as_slice(), [7, 2, 3]);
197 assert!(copy.retired.is_empty());
198 }
199}