Skip to content

Commit 5ea0b1f

Browse files
authored
ErasedPtr: drop Clone bound and allow cross-type nullptr casts (#4)
This PR allows converting between `void*` and non-clonable pointer types (`FILE*`, for example), and fixes cross-type nullptr casts (casting a `nullptr void*` to any other pointer).
1 parent d53a460 commit 5ea0b1f

1 file changed

Lines changed: 41 additions & 27 deletions

File tree

libcc2rs/src/rc.rs

Lines changed: 41 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1009,11 +1009,12 @@ trait ErasedPtr: std::any::Any {
10091009
fn memcpy(&self, src: &dyn ErasedPtr, len: usize);
10101010
fn as_any(&self) -> &dyn std::any::Any;
10111011
fn equals(&self, other: &dyn ErasedPtr) -> Option<bool>;
1012+
fn is_null(&self) -> bool;
10121013
}
10131014

10141015
impl<T> ErasedPtr for Ptr<T>
10151016
where
1016-
T: Clone + ByteRepr + 'static,
1017+
T: ByteRepr + 'static,
10171018
Ptr<T>: PartialEq,
10181019
{
10191020
fn pointee_type_id(&self) -> std::any::TypeId {
@@ -1024,35 +1025,13 @@ where
10241025
if self.pointee_type_id() != src.pointee_type_id() {
10251026
panic!("memcpy: type mismatch");
10261027
}
1027-
10281028
let src_ptr = src
10291029
.as_any()
10301030
.downcast_ref::<Ptr<T>>()
10311031
.expect("memcpy: downcast to Ptr<T> failed");
1032-
1033-
let elem = std::mem::size_of::<T>();
1034-
1035-
if len == elem {
1036-
self.write(src_ptr.read());
1037-
return;
1038-
}
1039-
1040-
if elem != 0 && len.is_multiple_of(elem) {
1041-
let mut dst = self.clone();
1042-
let mut src_it = src_ptr.clone();
1043-
for _ in 0..(len / elem) {
1044-
dst.write(src_it.read());
1045-
dst += 1;
1046-
src_it += 1;
1047-
}
1048-
1049-
return;
1050-
}
1051-
1052-
panic!(
1053-
"memcpy: len {} not compatible with element size {}",
1054-
len, elem
1055-
);
1032+
let dst_bytes: Ptr<u8> = self.reinterpret_cast();
1033+
let src_bytes: Ptr<u8> = src_ptr.reinterpret_cast();
1034+
dst_bytes.memcpy(&src_bytes, len);
10561035
}
10571036

10581037
fn as_any(&self) -> &dyn std::any::Any {
@@ -1070,14 +1049,18 @@ where
10701049

10711050
None
10721051
}
1052+
1053+
fn is_null(&self) -> bool {
1054+
Ptr::is_null(self)
1055+
}
10731056
}
10741057

10751058
#[derive(Clone)]
10761059
pub struct AnyPtr {
10771060
ptr: Rc<dyn ErasedPtr>,
10781061
}
10791062

1080-
impl<T: Clone + ByteRepr + 'static> Ptr<T> {
1063+
impl<T: ByteRepr + 'static> Ptr<T> {
10811064
pub fn to_any(&self) -> AnyPtr {
10821065
AnyPtr {
10831066
ptr: Rc::new(self.clone()),
@@ -1093,6 +1076,9 @@ impl Default for AnyPtr {
10931076

10941077
impl AnyPtr {
10951078
pub fn cast<T: 'static>(&self) -> Option<Ptr<T>> {
1079+
if self.ptr.is_null() {
1080+
return Some(Ptr::<T>::null());
1081+
}
10961082
self.ptr.as_any().downcast_ref::<Ptr<T>>().cloned()
10971083
}
10981084

@@ -1286,4 +1272,32 @@ mod tests {
12861272

12871273
p.delete();
12881274
}
1275+
1276+
#[test]
1277+
fn anyptr_null_cast() {
1278+
// void* nullptr
1279+
let any = Ptr::<()>::null().to_any();
1280+
let p: Option<Ptr<u32>> = any.cast::<u32>();
1281+
assert!(p.is_some());
1282+
assert!(p.unwrap().is_null());
1283+
1284+
let p2: Option<Ptr<u8>> = any.cast::<u8>();
1285+
assert!(p2.is_some());
1286+
assert!(p2.unwrap().is_null());
1287+
1288+
// int* nullptr
1289+
let any2 = Ptr::<i32>::null().to_any();
1290+
let p3: Option<Ptr<f32>> = any2.cast::<f32>();
1291+
assert!(p3.is_some());
1292+
assert!(p3.unwrap().is_null());
1293+
}
1294+
1295+
#[test]
1296+
fn to_any_without_clone() {
1297+
let p: Ptr<std::fs::File> = Ptr::null(); // std::fs::File is not Clone
1298+
let any = p.to_any();
1299+
let recovered = any.cast::<std::fs::File>();
1300+
assert!(recovered.is_some());
1301+
assert!(recovered.unwrap().is_null());
1302+
}
12891303
}

0 commit comments

Comments
 (0)