Skip to content

Commit 963701d

Browse files
authored
Memcpy a pointer to a struct through ErasedPtr (#207)
`AnyPtr::reinterpret_cast` was matching against a hardcoded list of types. This PR adds ErasedPtr::as_bytes which is a `reinterpret_cast::<u8>` so that memcpy/memset a pointer to a struct works. I also simplified the implementation for ErasedPtr::equals in this refactor.
1 parent 892566f commit 963701d

5 files changed

Lines changed: 151 additions & 83 deletions

File tree

libcc2rs/src/fn_ptr.rs

Lines changed: 5 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use std::marker::PhantomData;
66
use std::ops::Deref;
77
use std::rc::Rc;
88

9-
use crate::rc::{AnyPtr, ErasedPtr};
9+
use crate::rc::{AnyPtr, ErasedPtr, Ptr};
1010
use crate::reinterpret::ByteRepr;
1111

1212
pub trait FnAddr {
@@ -146,20 +146,14 @@ impl<T> Eq for FnPtr<T> {}
146146
impl<T: 'static> ByteRepr for FnPtr<T> {}
147147

148148
impl<T: 'static> ErasedPtr for FnPtr<T> {
149-
fn pointee_type_id(&self) -> TypeId {
150-
TypeId::of::<T>()
151-
}
152-
fn memcpy(&self, _src: &dyn ErasedPtr, _len: usize) {
153-
panic!("memcpy not supported on fn pointer");
149+
fn as_bytes(&self) -> Ptr<u8> {
150+
panic!("byte view not supported on fn pointer");
154151
}
155152
fn as_any(&self) -> &dyn Any {
156153
self
157154
}
158-
fn equals(&self, other: &dyn ErasedPtr) -> Option<bool> {
159-
if self.pointee_type_id() != other.pointee_type_id() {
160-
return None;
161-
}
162-
other.as_any().downcast_ref::<FnPtr<T>>().map(|o| self == o)
155+
fn equals(&self, other: &dyn ErasedPtr) -> bool {
156+
other.as_any().downcast_ref::<FnPtr<T>>() == Some(self)
163157
}
164158
fn is_null(&self) -> bool {
165159
FnPtr::is_null(self)

libcc2rs/src/rc.rs

Lines changed: 18 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -1046,49 +1046,33 @@ impl Ptr<u8> {
10461046
}
10471047

10481048
pub(crate) trait ErasedPtr: std::any::Any {
1049-
fn pointee_type_id(&self) -> std::any::TypeId;
1050-
fn memcpy(&self, src: &dyn ErasedPtr, len: usize);
1049+
fn as_bytes(&self) -> Ptr<u8>;
10511050
fn as_any(&self) -> &dyn std::any::Any;
1052-
fn equals(&self, other: &dyn ErasedPtr) -> Option<bool>;
1051+
fn equals(&self, other: &dyn ErasedPtr) -> bool;
10531052
fn is_null(&self) -> bool;
10541053
}
10551054

1055+
impl PartialEq for dyn ErasedPtr {
1056+
fn eq(&self, other: &Self) -> bool {
1057+
self.equals(other)
1058+
}
1059+
}
1060+
10561061
impl<T> ErasedPtr for Ptr<T>
10571062
where
10581063
T: ByteRepr + 'static,
10591064
Ptr<T>: PartialEq,
10601065
{
1061-
fn pointee_type_id(&self) -> std::any::TypeId {
1062-
std::any::TypeId::of::<T>()
1063-
}
1064-
1065-
fn memcpy(&self, src: &dyn ErasedPtr, len: usize) {
1066-
if self.pointee_type_id() != src.pointee_type_id() {
1067-
panic!("memcpy: type mismatch");
1068-
}
1069-
let src_ptr = src
1070-
.as_any()
1071-
.downcast_ref::<Ptr<T>>()
1072-
.expect("memcpy: downcast to Ptr<T> failed");
1073-
let dst_bytes: Ptr<u8> = self.reinterpret_cast();
1074-
let src_bytes: Ptr<u8> = src_ptr.reinterpret_cast();
1075-
dst_bytes.memcpy(&src_bytes, len);
1066+
fn as_bytes(&self) -> Ptr<u8> {
1067+
self.reinterpret_cast::<u8>()
10761068
}
10771069

10781070
fn as_any(&self) -> &dyn std::any::Any {
10791071
self
10801072
}
10811073

1082-
fn equals(&self, other: &dyn ErasedPtr) -> Option<bool> {
1083-
if self.pointee_type_id() != other.pointee_type_id() {
1084-
return None;
1085-
}
1086-
1087-
if let Some(other_ptr) = other.as_any().downcast_ref::<Ptr<T>>() {
1088-
return Some(self == other_ptr);
1089-
}
1090-
1091-
None
1074+
fn equals(&self, other: &dyn ErasedPtr) -> bool {
1075+
other.as_any().downcast_ref::<Ptr<T>>() == Some(self)
10921076
}
10931077

10941078
fn is_null(&self) -> bool {
@@ -1122,66 +1106,28 @@ impl AnyPtr {
11221106
}
11231107
self.ptr.as_any().downcast_ref::<Ptr<T>>().cloned()
11241108
}
1125-
1126-
pub fn reinterpret_cast<T: ByteRepr>(&self) -> Ptr<T> {
1127-
macro_rules! try_src {
1128-
($ty:ty) => {{
1129-
if let Some(p) = self.cast::<$ty>() {
1130-
return p.reinterpret_cast::<T>();
1131-
}
1132-
if let Some(pv) = self.cast::<Vec<$ty>>() {
1133-
return pv.reinterpret_cast::<T>();
1134-
}
1135-
}};
1136-
}
1137-
1138-
try_src!(u8);
1139-
try_src!(i8);
1140-
try_src!(u16);
1141-
try_src!(i16);
1142-
try_src!(u32);
1143-
try_src!(i32);
1144-
try_src!(u64);
1145-
try_src!(i64);
1146-
try_src!(usize);
1147-
try_src!(isize);
1148-
1149-
panic!("reinterpret_cast: unsupported AnyPtr source");
1150-
}
11511109
}
11521110

11531111
impl PartialEq for AnyPtr {
11541112
fn eq(&self, other: &Self) -> bool {
1155-
let lhs: &dyn ErasedPtr = self.ptr.as_ref();
1156-
let rhs: &dyn ErasedPtr = other.ptr.as_ref();
1157-
1158-
lhs.equals(rhs).unwrap_or_default()
1113+
*self.ptr == *other.ptr
11591114
}
11601115
}
11611116

11621117
impl AnyPtr {
11631118
pub fn memcpy(&self, src: &AnyPtr, len: usize) {
1164-
let dst_erased = &*self.ptr;
1165-
let src_erased = &*src.ptr;
1166-
1167-
if dst_erased.pointee_type_id() == src_erased.pointee_type_id() {
1168-
dst_erased.memcpy(src_erased, len);
1169-
return;
1170-
}
1171-
1172-
let dst_u8: Ptr<u8> = self.reinterpret_cast();
1173-
let src_u8: Ptr<u8> = src.reinterpret_cast();
1119+
let dst_u8 = self.ptr.as_bytes();
1120+
let src_u8 = src.ptr.as_bytes();
11741121
dst_u8.memcpy(&src_u8, len);
11751122
}
11761123

11771124
pub fn memset(&self, value: u8, num: usize) {
1178-
let dst_u8: Ptr<u8> = self.reinterpret_cast();
1179-
dst_u8.memset(value, num);
1125+
self.ptr.as_bytes().memset(value, num);
11801126
}
11811127

11821128
pub fn memcmp(&self, other: &AnyPtr, len: usize) -> i32 {
1183-
let a: Ptr<u8> = self.reinterpret_cast();
1184-
let b: Ptr<u8> = other.reinterpret_cast();
1129+
let a = self.ptr.as_bytes();
1130+
let b = other.ptr.as_bytes();
11851131
a.memcmp(&b, len)
11861132
}
11871133
}

tests/unit/memcpy_struct_bytes.c

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#include <assert.h>
2+
#include <stdint.h>
3+
#include <string.h>
4+
5+
struct point {
6+
int32_t x;
7+
int32_t y;
8+
};
9+
10+
int main(void) {
11+
struct point src = {3, 7};
12+
13+
unsigned char buf[sizeof(struct point)];
14+
memcpy(buf, &src, sizeof(buf));
15+
16+
struct point dst;
17+
memcpy(&dst, buf, sizeof(dst));
18+
19+
assert(dst.x == 3);
20+
assert(dst.y == 7);
21+
22+
return 0;
23+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
extern crate libcc2rs;
2+
use libcc2rs::*;
3+
use std::cell::RefCell;
4+
use std::collections::BTreeMap;
5+
use std::io::prelude::*;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::AsFd;
8+
use std::rc::{Rc, Weak};
9+
#[derive(Default)]
10+
pub struct point {
11+
pub x: Value<i32>,
12+
pub y: Value<i32>,
13+
}
14+
impl ByteRepr for point {
15+
fn byte_size() -> usize {
16+
8
17+
}
18+
fn to_bytes(&self, buf: &mut [u8]) {
19+
(*self.x.borrow()).to_bytes(&mut buf[0..4]);
20+
(*self.y.borrow()).to_bytes(&mut buf[4..8]);
21+
}
22+
fn from_bytes(buf: &[u8]) -> Self {
23+
Self {
24+
x: Rc::new(RefCell::new(<i32>::from_bytes(&buf[0..4]))),
25+
y: Rc::new(RefCell::new(<i32>::from_bytes(&buf[4..8]))),
26+
}
27+
}
28+
}
29+
pub fn main() {
30+
std::process::exit(main_0());
31+
}
32+
fn main_0() -> i32 {
33+
let src: Value<point> = Rc::new(RefCell::new(point {
34+
x: Rc::new(RefCell::new(3)),
35+
y: Rc::new(RefCell::new(7)),
36+
}));
37+
let buf: Value<Box<[u8]>> = Rc::new(RefCell::new(
38+
(0..8).map(|_| <u8>::default()).collect::<Box<[u8]>>(),
39+
));
40+
{
41+
((buf.as_pointer() as Ptr<u8>) as Ptr<u8>).to_any().memcpy(
42+
&((src.as_pointer()) as Ptr<point>).to_any(),
43+
::std::mem::size_of::<[u8; 8]>() as usize,
44+
);
45+
((buf.as_pointer() as Ptr<u8>) as Ptr<u8>).to_any().clone()
46+
};
47+
let dst: Value<point> = <Value<point>>::default();
48+
{
49+
((dst.as_pointer()) as Ptr<point>).to_any().memcpy(
50+
&((buf.as_pointer() as Ptr<u8>) as Ptr<u8>).to_any(),
51+
8usize as usize,
52+
);
53+
((dst.as_pointer()) as Ptr<point>).to_any().clone()
54+
};
55+
assert!(((((*(*dst.borrow()).x.borrow()) == 3) as i32) != 0));
56+
assert!(((((*(*dst.borrow()).y.borrow()) == 7) as i32) != 0));
57+
return 0;
58+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
extern crate libc;
2+
use libc::*;
3+
extern crate libcc2rs;
4+
use libcc2rs::*;
5+
use std::collections::BTreeMap;
6+
use std::io::{Read, Seek, Write};
7+
use std::os::fd::{AsFd, FromRawFd, IntoRawFd};
8+
use std::rc::Rc;
9+
#[repr(C)]
10+
#[derive(Copy, Clone, Default)]
11+
pub struct point {
12+
pub x: i32,
13+
pub y: i32,
14+
}
15+
pub fn main() {
16+
unsafe {
17+
std::process::exit(main_0() as i32);
18+
}
19+
}
20+
unsafe fn main_0() -> i32 {
21+
let mut src: point = point { x: 3, y: 7 };
22+
let mut buf: [u8; 8] = [0_u8; 8];
23+
{
24+
if ::std::mem::size_of::<[u8; 8]>() != 0 {
25+
::std::ptr::copy_nonoverlapping(
26+
((&mut src as *mut point) as *const point as *const ::libc::c_void),
27+
(buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void),
28+
::std::mem::size_of::<[u8; 8]>() as usize,
29+
)
30+
}
31+
(buf.as_mut_ptr() as *mut u8 as *mut ::libc::c_void)
32+
};
33+
let mut dst: point = <point>::default();
34+
{
35+
if ::std::mem::size_of::<point>() != 0 {
36+
::std::ptr::copy_nonoverlapping(
37+
(buf.as_mut_ptr() as *const u8 as *const ::libc::c_void),
38+
((&mut dst as *mut point) as *mut point as *mut ::libc::c_void),
39+
::std::mem::size_of::<point>() as usize,
40+
)
41+
}
42+
((&mut dst as *mut point) as *mut point as *mut ::libc::c_void)
43+
};
44+
assert!(((((dst.x) == (3)) as i32) != 0));
45+
assert!(((((dst.y) == (7)) as i32) != 0));
46+
return 0;
47+
}

0 commit comments

Comments
 (0)