Skip to content

Commit d82d983

Browse files
authored
Use C size for serialization/deserialization (#208)
This adds a new method in ByteRepr called byte_size. It is used to describe the C/C++ byte size of the object and it's used for serializatoin/deserialization through from/to_bytes. In refcount, Rust structs have a different size than the C/C++ structs because each field is wrapped in `Value<>`. For example: ```cpp struct S { int32_t a; int32_t b; } sizeof(S) == 8 ``` becomes ```rs struct S { a: Value<i32>, b: Value<i32> } size_of(S) == 16 // one Rc is 8 ``` This difference in size breaks ByteRepr for structs. To do correct serialization we need the C/C++ size. Codegen embeds this into the generated code using the ByteRepr::byte_size method. Then, when a reinterpreted pointer is created, ReinterpretedView::elem_byte_size saves ByteRepr::byte_size. This is an optimization so that the ByteRepr trait is not exposed to all Ptr methods, even into methods that don't have to do with reinterpreted pointers. The goal of this is to support structs inside unions. Structs, as any other type that takes part in an union, is a reinterpreted view into the byte buffer of the union.
1 parent 8ab5569 commit d82d983

52 files changed

Lines changed: 514 additions & 73 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

cpp2rust/converter/converter_lib.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,43 @@ bool IsMut(clang::QualType qual_type) {
200200
qual_type->getPointeeType().isConstQualified());
201201
}
202202

203+
bool TypeImplementsByteRepr(clang::QualType qt) {
204+
if (qt->isIntegerType() || qt->isFloatingType() || qt->isEnumeralType()) {
205+
return true;
206+
}
207+
if (const auto *arr = qt->getAsArrayTypeUnsafe()) {
208+
return TypeImplementsByteRepr(arr->getElementType());
209+
}
210+
if (const auto *rd = qt->getAsRecordDecl()) {
211+
if (rd->getASTContext().getSourceManager().isInSystemHeader(
212+
rd->getLocation())) {
213+
return false;
214+
}
215+
if (rd->isUnion()) {
216+
return false;
217+
}
218+
for (const auto *field : rd->fields()) {
219+
if (!TypeImplementsByteRepr(field->getType())) {
220+
return false;
221+
}
222+
}
223+
return true;
224+
}
225+
return false;
226+
}
227+
228+
bool RustSizeDivergesFromC(clang::QualType qt) {
229+
qt = qt.getCanonicalType();
230+
// Records have Rc<RefCell<>> fields that diverge from the C size
231+
if (qt->isRecordType()) {
232+
return true;
233+
}
234+
if (auto *arr = qt->getAsArrayTypeUnsafe()) {
235+
return RustSizeDivergesFromC(arr->getElementType());
236+
}
237+
return false;
238+
}
239+
203240
bool IsMutatingCall(const clang::CallExpr *expr) {
204241
if (auto *callee = expr->getDirectCallee()) {
205242
if (auto *method = clang::dyn_cast<clang::CXXMethodDecl>(callee)) {

cpp2rust/converter/converter_lib.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,10 @@ bool IsUnsignedArithOp(const clang::BinaryOperator *expr);
5252

5353
bool IsMut(clang::QualType qual_type);
5454

55+
bool TypeImplementsByteRepr(clang::QualType qt);
56+
57+
bool RustSizeDivergesFromC(clang::QualType qt);
58+
5559
bool IsMutatingCall(const clang::CallExpr *expr);
5660

5761
bool IsOverloadedFunction(const clang::FunctionDecl *decl);

cpp2rust/converter/models/converter_refcount.cpp

Lines changed: 22 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -545,28 +545,10 @@ void ConverterRefCount::AddDropTrait(const clang::CXXRecordDecl *decl) {
545545
StrCat('}');
546546
}
547547

548-
static bool recordImplementsByteRepr(const clang::RecordDecl *decl) {
549-
if (decl->isUnion()) {
550-
return false;
551-
}
552-
553-
// ByteRepr is only supported for user-defined structs that contain ByteRepr
554-
// fields.
555-
for (auto *f : decl->fields()) {
556-
auto qt = f->getType();
557-
if (!qt->isIntegerType() && !qt->isFloatingType() &&
558-
!qt->isEnumeralType()) {
559-
return false;
560-
}
561-
}
562-
563-
return true;
564-
}
565-
566548
void ConverterRefCount::AddByteReprTrait(const clang::RecordDecl *decl) {
567549
auto struct_name = GetRecordName(decl);
568550

569-
if (!recordImplementsByteRepr(decl)) {
551+
if (!TypeImplementsByteRepr(ctx_.getCanonicalTagType(decl))) {
570552
StrCat(std::format("impl ByteRepr for {}", struct_name));
571553
PushBrace brace(*this);
572554
return;
@@ -577,6 +559,9 @@ void ConverterRefCount::AddByteReprTrait(const clang::RecordDecl *decl) {
577559

578560
const auto &layout = ctx_.getASTRecordLayout(decl);
579561

562+
StrCat(std::format("fn byte_size() -> usize {{ {} }}",
563+
ctx_.getTypeSize(ctx_.getCanonicalTagType(decl)) / 8));
564+
580565
StrCat("fn to_bytes(&self, buf: &mut [u8])");
581566
{
582567
PushBrace fn_brace(*this);
@@ -600,9 +585,12 @@ void ConverterRefCount::AddByteReprTrait(const clang::RecordDecl *decl) {
600585
for (auto *field : decl->fields()) {
601586
auto byte_off = layout.getFieldOffset(idx) / 8;
602587
auto byte_size = ctx_.getTypeSize(field->getType()) / 8;
588+
PushConversionKind push(*this, ConversionKind::FullRefCount);
589+
std::string storage_ty = ToString(field->getType());
590+
Unwrap(storage_ty, "Value<", ">");
603591
StrCat(std::format(
604592
"{}: Rc::new(RefCell::new(<{}>::from_bytes(&buf[{}..{}]))),",
605-
GetNamedDeclAsString(field), Mapper::Map(field->getType()), byte_off,
593+
GetNamedDeclAsString(field), storage_ty, byte_off,
606594
byte_off + byte_size));
607595
++idx;
608596
}
@@ -1322,6 +1310,20 @@ bool ConverterRefCount::VisitExplicitCastExpr(clang::ExplicitCastExpr *expr) {
13221310
}
13231311
}
13241312

1313+
bool ConverterRefCount::VisitUnaryExprOrTypeTraitExpr(
1314+
clang::UnaryExprOrTypeTraitExpr *expr) {
1315+
if (expr->getKind() == clang::UnaryExprOrTypeTrait::UETT_SizeOf) {
1316+
auto arg_type = expr->isArgumentType() ? expr->getArgumentType()
1317+
: expr->getArgumentExpr()->getType();
1318+
if (RustSizeDivergesFromC(arg_type)) {
1319+
StrCat(std::format("{}usize", ctx_.getTypeSize(arg_type) / 8));
1320+
computed_expr_type_ = ComputedExprType::FreshValue;
1321+
return false;
1322+
}
1323+
}
1324+
return Converter::VisitUnaryExprOrTypeTraitExpr(expr);
1325+
}
1326+
13251327
bool ConverterRefCount::VisitStmtExpr(clang::StmtExpr *expr) {
13261328
PushConversionKind push(*this, ConversionKind::FullRefCount);
13271329
return Converter::VisitStmtExpr(expr);

cpp2rust/converter/models/converter_refcount.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ class ConverterRefCount final : public Converter {
4343

4444
void AddByteReprTrait(const clang::EnumDecl *decl) override;
4545

46+
bool
47+
VisitUnaryExprOrTypeTraitExpr(clang::UnaryExprOrTypeTraitExpr *expr) override;
48+
4649
void AddDefaultTrait(const clang::RecordDecl *decl) override;
4750

4851
void AddDefaultTraitForUnion(const clang::RecordDecl *decl) override;

libcc2rs/src/rc.rs

Lines changed: 46 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,13 @@ use crate::reinterpret::{ByteRepr, OriginalAlloc, SingleOriginalAlloc, SliceOrig
1616

1717
pub type Value<T> = Rc<RefCell<T>>;
1818

19+
struct ReinterpretedView {
20+
// Pointer to the source of reinterpret
21+
alloc: Rc<dyn OriginalAlloc>,
22+
// C++ size of the reinterpreted view
23+
elem_byte_size: usize,
24+
}
25+
1926
#[derive(Default)]
2027
enum PtrKind<T> {
2128
#[default]
@@ -25,7 +32,7 @@ enum PtrKind<T> {
2532
HeapSingle(Weak<RefCell<T>>),
2633
HeapArray(Weak<RefCell<Box<[T]>>>),
2734
Vec(Weak<RefCell<Vec<T>>>),
28-
Reinterpreted(Rc<dyn OriginalAlloc>),
35+
Reinterpreted(Rc<ReinterpretedView>),
2936
}
3037

3138
pub enum StrongPtr<T> {
@@ -59,7 +66,7 @@ impl<T: ByteRepr> StrongPtr<T> {
5966
cell,
6067
} => {
6168
// Read-through: always re-read from the original allocation.
62-
let mut buf = vec![0u8; std::mem::size_of::<T>()];
69+
let mut buf = vec![0u8; T::byte_size()];
6370
alloc.read_bytes(*byte_offset, &mut buf);
6471
*cell.borrow_mut() = Some(T::from_bytes(&buf));
6572
Ref::map(cell.borrow(), |opt| opt.as_ref().unwrap())
@@ -78,7 +85,7 @@ impl<T> fmt::Debug for PtrKind<T> {
7885
PtrKind::StackArray(w) => write!(f, "StackArray({:?})", w.as_ptr()),
7986
PtrKind::HeapArray(w) => write!(f, "HeapArray({:?})", w.as_ptr()),
8087
PtrKind::Reinterpreted(data) => {
81-
write!(f, "Reinterpreted(0x{:x})", data.address())
88+
write!(f, "Reinterpreted(0x{:x})", data.alloc.address())
8289
}
8390
}
8491
}
@@ -105,7 +112,7 @@ impl<T> PtrKind<T> {
105112
PtrKind::StackSingle(w) | PtrKind::HeapSingle(w) => w.as_ptr() as usize,
106113
PtrKind::Vec(w) => w.as_ptr() as usize,
107114
PtrKind::StackArray(w) | PtrKind::HeapArray(w) => w.as_ptr() as usize,
108-
PtrKind::Reinterpreted(data) => data.address(),
115+
PtrKind::Reinterpreted(data) => data.alloc.address(),
109116
}
110117
}
111118
}
@@ -251,40 +258,40 @@ impl<T> Ptr<T> {
251258
#[inline]
252259
fn elem_step(&self) -> usize {
253260
match &self.kind {
254-
PtrKind::Reinterpreted(_) => std::mem::size_of::<T>(),
261+
PtrKind::Reinterpreted(data) => data.elem_byte_size,
255262
_ => 1,
256263
}
257264
}
258265

259266
#[inline]
260267
pub fn len(&self) -> usize {
261-
match self.kind {
268+
match &self.kind {
262269
PtrKind::Null => 0,
263270
PtrKind::StackSingle(_) | PtrKind::HeapSingle(_) => 1,
264-
PtrKind::Vec(ref weak) => weak.upgrade().expect("ub: dangling pointer").borrow().len(),
265-
PtrKind::StackArray(ref weak) | PtrKind::HeapArray(ref weak) => {
271+
PtrKind::Vec(weak) => weak.upgrade().expect("ub: dangling pointer").borrow().len(),
272+
PtrKind::StackArray(weak) | PtrKind::HeapArray(weak) => {
266273
weak.upgrade().expect("ub: dangling pointer").borrow().len()
267274
}
268-
PtrKind::Reinterpreted(ref data) => data.total_byte_len() / std::mem::size_of::<T>(),
275+
PtrKind::Reinterpreted(data) => data.alloc.total_byte_len() / data.elem_byte_size,
269276
}
270277
}
271278

272279
#[inline]
273280
pub fn is_empty(&self) -> bool {
274-
match self.kind {
281+
match &self.kind {
275282
PtrKind::Null => true,
276283
PtrKind::StackSingle(_) | PtrKind::HeapSingle(_) => false,
277-
PtrKind::Vec(ref weak) => weak
284+
PtrKind::Vec(weak) => weak
278285
.upgrade()
279286
.expect("ub: dangling pointer")
280287
.borrow()
281288
.is_empty(),
282-
PtrKind::StackArray(ref weak) | PtrKind::HeapArray(ref weak) => weak
289+
PtrKind::StackArray(weak) | PtrKind::HeapArray(weak) => weak
283290
.upgrade()
284291
.expect("ub: dangling pointer")
285292
.borrow()
286293
.is_empty(),
287-
PtrKind::Reinterpreted(ref data) => self.offset >= data.total_byte_len(),
294+
PtrKind::Reinterpreted(data) => self.offset >= data.alloc.total_byte_len(),
288295
}
289296
}
290297

@@ -340,7 +347,7 @@ impl<T> Ptr<T> {
340347
offset: self.offset,
341348
},
342349
PtrKind::Reinterpreted(data) => StrongPtr::Reinterpreted {
343-
alloc: Rc::clone(data),
350+
alloc: Rc::clone(&data.alloc),
344351
byte_offset: self.offset,
345352
cell: RefCell::new(None),
346353
},
@@ -366,9 +373,9 @@ impl<T> Ptr<T> {
366373
rc.borrow_mut()[self.offset] = value;
367374
}
368375
PtrKind::Reinterpreted(data) => {
369-
let mut buf = vec![0u8; std::mem::size_of::<T>()];
376+
let mut buf = vec![0u8; T::byte_size()];
370377
value.to_bytes(&mut buf);
371-
data.write_bytes(self.offset, &buf);
378+
data.alloc.write_bytes(self.offset, &buf);
372379
}
373380
}
374381
}
@@ -391,30 +398,34 @@ impl<T> Ptr<T> {
391398
return self_any.downcast_ref::<Ptr<U>>().unwrap().clone();
392399
}
393400

394-
if std::mem::size_of::<U>() == 0 {
401+
if U::byte_size() == 0 {
395402
panic!("cannot reinterpret_cast to zero-sized type");
396403
}
397404

405+
let src_byte_off = self.offset.wrapping_mul(T::byte_size());
398406
let (alloc, abs_byte_off): (Rc<dyn OriginalAlloc>, usize) = match &self.kind {
399407
PtrKind::Null => return Ptr::null(),
400408
PtrKind::StackSingle(weak) | PtrKind::HeapSingle(weak) => (
401409
Rc::new(SingleOriginalAlloc { weak: weak.clone() }),
402-
self.byte_offset(),
410+
src_byte_off,
403411
),
404412
PtrKind::Vec(weak) => (
405413
Rc::new(SliceOriginalAlloc { weak: weak.clone() }),
406-
self.byte_offset(),
414+
src_byte_off,
407415
),
408416
PtrKind::StackArray(weak) | PtrKind::HeapArray(weak) => (
409417
Rc::new(SliceOriginalAlloc { weak: weak.clone() }),
410-
self.byte_offset(),
418+
src_byte_off,
411419
),
412-
PtrKind::Reinterpreted(data) => (Rc::clone(data), self.offset),
420+
PtrKind::Reinterpreted(data) => (Rc::clone(&data.alloc), self.offset),
413421
};
414422

415423
Ptr {
416424
offset: abs_byte_off,
417-
kind: PtrKind::Reinterpreted(alloc),
425+
kind: PtrKind::Reinterpreted(Rc::new(ReinterpretedView {
426+
alloc,
427+
elem_byte_size: U::byte_size(),
428+
})),
418429
}
419430
}
420431
}
@@ -442,12 +453,12 @@ impl<T> Ptr<T> {
442453
f(&mut borrow[self.offset])
443454
}
444455
PtrKind::Reinterpreted(data) => {
445-
let mut buf = vec![0u8; std::mem::size_of::<T>()];
446-
data.read_bytes(self.offset, &mut buf);
456+
let mut buf = vec![0u8; T::byte_size()];
457+
data.alloc.read_bytes(self.offset, &mut buf);
447458
let mut val = T::from_bytes(&buf);
448459
let ret = f(&mut val);
449460
val.to_bytes(&mut buf);
450-
data.write_bytes(self.offset, &buf);
461+
data.alloc.write_bytes(self.offset, &buf);
451462
ret
452463
}
453464
}
@@ -475,8 +486,8 @@ impl<T> Ptr<T> {
475486
f(&borrow[self.offset])
476487
}
477488
PtrKind::Reinterpreted(data) => {
478-
let mut buf = vec![0u8; std::mem::size_of::<T>()];
479-
data.read_bytes(self.offset, &mut buf);
489+
let mut buf = vec![0u8; T::byte_size()];
490+
data.alloc.read_bytes(self.offset, &mut buf);
480491
let val = T::from_bytes(&buf);
481492
f(&val)
482493
}
@@ -502,8 +513,8 @@ impl<T: Clone + ByteRepr> Ptr<T> {
502513
weak.upgrade().expect("ub: dangling pointer").borrow()[self.offset].clone()
503514
}
504515
PtrKind::Reinterpreted(ref data) => {
505-
let mut buf = vec![0u8; std::mem::size_of::<T>()];
506-
data.read_bytes(self.offset, &mut buf);
516+
let mut buf = vec![0u8; T::byte_size()];
517+
data.alloc.read_bytes(self.offset, &mut buf);
507518
T::from_bytes(&buf)
508519
}
509520
}
@@ -535,7 +546,7 @@ impl<T: std::cmp::Ord> Ptr<T> {
535546
let strong = weak.upgrade().expect("ub: dangling pointer");
536547
(*strong.borrow_mut())[self.get_offset()..last].sort();
537548
}
538-
PtrKind::Reinterpreted(..) => {
549+
PtrKind::Reinterpreted(_) => {
539550
panic!("sorting not supported for reinterpreted pointers")
540551
}
541552
}
@@ -580,7 +591,7 @@ impl<T: Clone> Ptr<T> {
580591
let mut borrow = strong.borrow_mut();
581592
sort(&mut borrow, self.get_offset(), last, &mut cmp);
582593
}
583-
PtrKind::Reinterpreted(..) => {
594+
PtrKind::Reinterpreted(_) => {
584595
panic!("sorting not supported for reinterpreted pointers")
585596
}
586597
}
@@ -856,7 +867,7 @@ impl<T> ToOwnedOption<T, T> for Ptr<T> {
856867
}
857868
PtrKind::Vec(_) => panic!("Can't own a vector"),
858869
PtrKind::HeapArray(_) => panic!("Can't own an array variable as single"),
859-
PtrKind::Reinterpreted(..) => panic!("Can't own a reinterpreted pointer"),
870+
PtrKind::Reinterpreted(_) => panic!("Can't own a reinterpreted pointer"),
860871
}
861872
}
862873
}
@@ -882,7 +893,7 @@ impl<T> ToOwnedOption<T, Box<[T]>> for Ptr<T> {
882893
}
883894
PtrKind::Vec(_) => panic!("Can't own a vector"),
884895
PtrKind::HeapSingle(_) => panic!("Can't own a single variable as an array"),
885-
PtrKind::Reinterpreted(..) => panic!("Can't own a reinterpreted pointer"),
896+
PtrKind::Reinterpreted(_) => panic!("Can't own a reinterpreted pointer"),
886897
}
887898
}
888899
}
@@ -898,7 +909,7 @@ impl<T> fmt::Debug for Ptr<T> {
898909
(Weak::as_ptr(w) as usize).wrapping_add(self.byte_offset())
899910
}
900911
PtrKind::Vec(w) => (Weak::as_ptr(w) as usize).wrapping_add(self.byte_offset()),
901-
PtrKind::Reinterpreted(data) => data.address().wrapping_add(self.byte_offset()),
912+
PtrKind::Reinterpreted(data) => data.alloc.address().wrapping_add(self.byte_offset()),
902913
};
903914
write!(f, "0x{:x}", addr)
904915
}
@@ -996,7 +1007,7 @@ impl Ptr<u8> {
9961007
}
9971008
PtrKind::Reinterpreted(ref data) => {
9981009
let mut buf = vec![0u8; end.wrapping_sub(start)];
999-
data.read_bytes(start, &mut buf);
1010+
data.alloc.read_bytes(start, &mut buf);
10001011
buf
10011012
}
10021013
}

0 commit comments

Comments
 (0)