Add macro for padding struct
This commit is contained in:
parent
84bced252b
commit
d1c9d119b3
|
|
@ -1401,6 +1401,16 @@ version = "4.2.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48dd4f4a2c8405440fd0462561f0e5806bd0f77e86f51c761481bdd4018b545e"
|
||||
|
||||
[[package]]
|
||||
name = "padding-struct"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.101",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ members = [
|
|||
"ostd/libs/linux-bzimage/setup",
|
||||
"ostd/libs/ostd-macros",
|
||||
"ostd/libs/ostd-test",
|
||||
"ostd/libs/padding-struct",
|
||||
"kernel",
|
||||
"kernel/comps/block",
|
||||
"kernel/comps/cmdline",
|
||||
|
|
|
|||
1
Makefile
1
Makefile
|
|
@ -213,6 +213,7 @@ NON_OSDK_CRATES := \
|
|||
ostd/libs/linux-bzimage/builder \
|
||||
ostd/libs/ostd-macros \
|
||||
ostd/libs/ostd-test \
|
||||
ostd/libs/padding-struct \
|
||||
kernel/libs/aster-rights \
|
||||
kernel/libs/aster-rights-proc \
|
||||
kernel/libs/atomic-integer-wrapper \
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
[package]
|
||||
name = "padding-struct"
|
||||
version = "0.2.0"
|
||||
description = "A procedural macro for automatically adding explicit padding fields to #[repr(C)] structs"
|
||||
readme = "README.md"
|
||||
repository.workspace = true
|
||||
license.workspace = true
|
||||
edition.workspace = true
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
proc-macro2.workspace = true
|
||||
quote.workspace = true
|
||||
syn.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
zerocopy.workspace = true
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
@ -0,0 +1,77 @@
|
|||
<!--
|
||||
To promote a "single source of truth", the content of `README.md` is also included in `lib.rs`
|
||||
as the crate-level documentation. So when writing this README, bear in mind that its content
|
||||
should be recognized correctly by both a Markdown renderer and the rustdoc tool.
|
||||
-->
|
||||
|
||||
# padding-struct
|
||||
|
||||
A Rust procedural macro for automatically adding explicit padding fields to `#[repr(C)]` structs.
|
||||
|
||||
## Overview
|
||||
|
||||
When working with `#[repr(C)]` structs,
|
||||
the Rust compiler automatically adds padding bytes to ensure proper alignment.
|
||||
The `#[padding_struct]` macro makes these padding bytes explicit
|
||||
by automatically generating padding fields in your struct definitions.
|
||||
|
||||
### Basic Example
|
||||
|
||||
```rust
|
||||
use padding_struct::padding_struct;
|
||||
|
||||
#[repr(C)]
|
||||
#[padding_struct]
|
||||
struct MyStruct {
|
||||
a: u8, // 1 byte
|
||||
b: u32, // 4 bytes (aligned to 4-byte boundary)
|
||||
c: u16, // 2 bytes
|
||||
}
|
||||
```
|
||||
|
||||
The macro generates a new struct with explicit padding bytes:
|
||||
|
||||
```rust
|
||||
// Padded struct (the one you'll use)
|
||||
#[repr(C)]
|
||||
struct MyStruct {
|
||||
a: u8,
|
||||
pub __pad1: [u8; 3], // padding before `b`
|
||||
b: u32,
|
||||
pub __pad2: [u8; 0], // no padding before `c`
|
||||
c: u16,
|
||||
pub __pad3: [u8; 2], // trailing padding
|
||||
}
|
||||
```
|
||||
|
||||
### Integration with zerocopy
|
||||
|
||||
The generated structs work seamlessly with `zerocopy` for safe transmutation:
|
||||
|
||||
```rust
|
||||
use padding_struct::padding_struct;
|
||||
use zerocopy::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[padding_struct]
|
||||
#[derive(Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)]
|
||||
struct SafeStruct {
|
||||
field1: u32,
|
||||
field2: u64,
|
||||
}
|
||||
|
||||
// Now you can safely cast to/from bytes
|
||||
let s = SafeStruct::new_zeroed();
|
||||
let bytes: &[u8] = s.as_bytes();
|
||||
assert_eq!(bytes.len(), size_of::<SafeStruct>());
|
||||
assert_eq!(bytes, &[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under MPL-2.0.
|
||||
|
||||
<!--
|
||||
External links.
|
||||
-->
|
||||
[`zerocopy`]: https://docs.rs/zerocopy/
|
||||
|
|
@ -0,0 +1,214 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
Attribute, Data, DataStruct, DeriveInput, Fields, Ident, Token, parse_macro_input,
|
||||
punctuated::Punctuated, spanned::Spanned,
|
||||
};
|
||||
|
||||
/// Checks if the struct has a `#[repr(C)]` attribute (possibly with other `repr` options)
|
||||
fn has_repr_c(attrs: &[Attribute]) -> bool {
|
||||
attrs.iter().any(|attr| {
|
||||
if attr.path().is_ident("repr") {
|
||||
// Parse the attribute using a custom parser
|
||||
let result = attr.parse_args_with(Punctuated::<syn::Meta, Token![,]>::parse_terminated);
|
||||
|
||||
if let Ok(list) = result {
|
||||
return list
|
||||
.iter()
|
||||
.any(|meta| matches!(meta, syn::Meta::Path(path) if path.is_ident("C")));
|
||||
}
|
||||
|
||||
false
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Extracts all `#[repr(...)]` attributes from the given attributes
|
||||
fn extract_repr_attrs(attrs: &[Attribute]) -> Vec<&Attribute> {
|
||||
attrs
|
||||
.iter()
|
||||
.filter(|attr| attr.path().is_ident("repr"))
|
||||
.collect()
|
||||
}
|
||||
|
||||
/// Procedural macro to automatically add padding fields to a `#[repr(C)]` struct.
|
||||
///
|
||||
/// This macro generates two structs:
|
||||
/// 1. A reference struct (prefixed and suffixed with `__`) containing the original fields without padding.
|
||||
/// 2. A padded struct (using the original name) with `__padN` padding fields after each original field.
|
||||
///
|
||||
/// # Padding Calculation Rules
|
||||
///
|
||||
/// - For non-last fields: padding size = next field's offset - current field's offset - current field's size.
|
||||
/// - For the last field: padding size = struct total size - current field's offset - current field's size.
|
||||
///
|
||||
/// # Requirements
|
||||
///
|
||||
/// - The struct must have a `#[repr(C)]` attribute.
|
||||
/// - The struct must have named fields.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use padding_struct::padding_struct;
|
||||
///
|
||||
/// #[repr(C)]
|
||||
/// #[padding_struct]
|
||||
/// struct MyStruct {
|
||||
/// a: u8,
|
||||
/// b: u32,
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// This generates code equivalent to:
|
||||
///
|
||||
/// ```rust
|
||||
/// use core::mem::offset_of;
|
||||
///
|
||||
/// #[repr(C)]
|
||||
/// struct __MyStruct__ {
|
||||
/// a: u8,
|
||||
/// b: u32,
|
||||
/// }
|
||||
///
|
||||
/// #[repr(C)]
|
||||
/// struct MyStruct {
|
||||
/// a: u8,
|
||||
/// pub __pad1:
|
||||
/// [u8; const { offset_of!(__MyStruct__, b) - offset_of!(__MyStruct__, a) - size_of::<u8>() }],
|
||||
/// b: u32,
|
||||
/// pub __pad2:
|
||||
/// [u8; const { size_of::<__MyStruct__>() - offset_of!(__MyStruct__, b) - size_of::<u32>() }],
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro_attribute]
|
||||
pub fn padding_struct(args: TokenStream, input: TokenStream) -> TokenStream {
|
||||
// Reject any provided arguments to the attribute
|
||||
if !args.is_empty() {
|
||||
panic!("`#[padding_struct]` does not accept any arguments");
|
||||
}
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
|
||||
// Ensure it's a struct
|
||||
let fields = match &input.data {
|
||||
Data::Struct(DataStruct {
|
||||
fields: Fields::Named(fields),
|
||||
..
|
||||
}) => &fields.named,
|
||||
_ => panic!("`#[padding_struct]` only supports named-field structs"),
|
||||
};
|
||||
|
||||
// Ensure #[repr(C)] is present
|
||||
if !has_repr_c(&input.attrs) {
|
||||
panic!("`#[padding_struct]` requires `#[repr(C)]` or `#[repr(C, ...)]` on struct");
|
||||
}
|
||||
|
||||
let name = &input.ident;
|
||||
let vis = &input.vis;
|
||||
let generics = &input.generics;
|
||||
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||
|
||||
let ref_name = Ident::new(&format!("__{}__", name), name.span());
|
||||
|
||||
// Extract all repr attributes for the reference struct
|
||||
let repr_attrs = extract_repr_attrs(&input.attrs);
|
||||
|
||||
// Generate reference struct (same fields, no padding, with all repr attributes)
|
||||
let ref_fields: Vec<_> = fields.iter().collect();
|
||||
let ref_struct = quote! {
|
||||
#(#repr_attrs)*
|
||||
#[allow(missing_docs)]
|
||||
#[doc(hidden)]
|
||||
struct #ref_name #impl_generics #where_clause {
|
||||
#(#ref_fields),*
|
||||
}
|
||||
};
|
||||
|
||||
// Filter attributes for the padded struct (keep all except #[padding_struct])
|
||||
let padded_attrs: Vec<_> = input
|
||||
.attrs
|
||||
.iter()
|
||||
.filter(|attr| !attr.path().is_ident("padding_struct"))
|
||||
.collect();
|
||||
|
||||
// Generate padded struct with inline const expressions
|
||||
let mut padded_fields = Vec::new();
|
||||
let field_vec: Vec<_> = fields.iter().collect();
|
||||
|
||||
for (i, field) in field_vec.iter().enumerate() {
|
||||
let field_name = &field.ident;
|
||||
let field_ty = &field.ty;
|
||||
let field_attrs = &field.attrs;
|
||||
let field_vis = &field.vis;
|
||||
|
||||
// Add original field with its attributes and comments
|
||||
padded_fields.push(quote! {
|
||||
#(#field_attrs)*
|
||||
#field_vis #field_name: #field_ty
|
||||
});
|
||||
|
||||
// Generate padding field with inline const expression
|
||||
let pad_num = i + 1;
|
||||
let pad_ident = Ident::new(&format!("__pad{}", pad_num), field.span());
|
||||
|
||||
let pad_size_expr = if i == field_vec.len() - 1 {
|
||||
// Last field: padding to end of struct
|
||||
quote! {
|
||||
::core::mem::size_of::<#ref_name #ty_generics>()
|
||||
- ::core::mem::offset_of!(#ref_name #ty_generics, #field_name)
|
||||
- ::core::mem::size_of::<#field_ty>()
|
||||
}
|
||||
} else {
|
||||
// Middle field: padding to next field
|
||||
let next_field = field_vec[i + 1];
|
||||
let next_field_name = &next_field.ident;
|
||||
quote! {
|
||||
::core::mem::offset_of!(#ref_name #ty_generics, #next_field_name)
|
||||
- ::core::mem::offset_of!(#ref_name #ty_generics, #field_name)
|
||||
- ::core::mem::size_of::<#field_ty>()
|
||||
}
|
||||
};
|
||||
|
||||
// Add padding field with inline const block
|
||||
padded_fields.push(quote! {
|
||||
#[allow(missing_docs)]
|
||||
pub #pad_ident: [u8; { #pad_size_expr }]
|
||||
});
|
||||
}
|
||||
|
||||
let padded_struct = quote! {
|
||||
#(#padded_attrs)*
|
||||
#vis struct #name #impl_generics #where_clause {
|
||||
#(#padded_fields),*
|
||||
}
|
||||
};
|
||||
|
||||
// Generate compile-time assertions to ensure size and alignment match
|
||||
let size_align_check = quote! {
|
||||
const _: () = {
|
||||
// Assert that sizes are equal
|
||||
const _: [(); ::core::mem::size_of::<#ref_name #ty_generics>()] =
|
||||
[(); ::core::mem::size_of::<#name #ty_generics>()];
|
||||
|
||||
// Assert that alignments are equal
|
||||
const _: [(); ::core::mem::align_of::<#ref_name #ty_generics>()] =
|
||||
[(); ::core::mem::align_of::<#name #ty_generics>()];
|
||||
};
|
||||
};
|
||||
|
||||
let expanded = quote! {
|
||||
#ref_struct
|
||||
|
||||
#padded_struct
|
||||
|
||||
#size_align_check
|
||||
};
|
||||
|
||||
TokenStream::from(expanded)
|
||||
}
|
||||
|
|
@ -0,0 +1,267 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::mem::offset_of;
|
||||
|
||||
use padding_struct::padding_struct;
|
||||
|
||||
/// Test basic padding functionality
|
||||
#[test]
|
||||
fn basic_padding() {
|
||||
#[repr(C)]
|
||||
#[padding_struct]
|
||||
struct TestStruct {
|
||||
a: u8,
|
||||
b: u32,
|
||||
c: u16,
|
||||
}
|
||||
|
||||
// Verify reference struct exists
|
||||
let _ref_struct = __TestStruct__ { a: 1, b: 2, c: 3 };
|
||||
|
||||
// Verify padded struct
|
||||
let padded = TestStruct {
|
||||
a: 1,
|
||||
__pad1: [0; {
|
||||
offset_of!(__TestStruct__, b) - offset_of!(__TestStruct__, a) - size_of::<u8>()
|
||||
}],
|
||||
b: 2,
|
||||
__pad2: [0; {
|
||||
offset_of!(__TestStruct__, c) - offset_of!(__TestStruct__, b) - size_of::<u32>()
|
||||
}],
|
||||
c: 3,
|
||||
__pad3: [0; {
|
||||
size_of::<__TestStruct__>() - offset_of!(__TestStruct__, c) - size_of::<u16>()
|
||||
}],
|
||||
};
|
||||
|
||||
assert_eq!(padded.a, 1);
|
||||
assert_eq!(padded.b, 2);
|
||||
assert_eq!(padded.c, 3);
|
||||
}
|
||||
|
||||
/// Test single field struct
|
||||
#[test]
|
||||
fn single_field() {
|
||||
#[repr(C)]
|
||||
#[padding_struct]
|
||||
struct SingleField {
|
||||
value: u64,
|
||||
}
|
||||
|
||||
let single = SingleField {
|
||||
value: 42,
|
||||
__pad1: [0; {
|
||||
size_of::<__SingleField__>() - offset_of!(__SingleField__, value) - size_of::<u64>()
|
||||
}],
|
||||
};
|
||||
|
||||
assert_eq!(single.value, 42);
|
||||
}
|
||||
|
||||
/// Test multiple fields struct
|
||||
#[test]
|
||||
fn multiple_fields() {
|
||||
#[repr(C)]
|
||||
#[padding_struct]
|
||||
struct MultiField {
|
||||
a: u8,
|
||||
b: u16,
|
||||
c: u32,
|
||||
d: u64,
|
||||
}
|
||||
|
||||
let multi = MultiField {
|
||||
a: 1,
|
||||
__pad1: [0; {
|
||||
offset_of!(__MultiField__, b) - offset_of!(__MultiField__, a) - size_of::<u8>()
|
||||
}],
|
||||
b: 2,
|
||||
__pad2: [0; {
|
||||
offset_of!(__MultiField__, c) - offset_of!(__MultiField__, b) - size_of::<u16>()
|
||||
}],
|
||||
c: 3,
|
||||
__pad3: [0; {
|
||||
offset_of!(__MultiField__, d) - offset_of!(__MultiField__, c) - size_of::<u32>()
|
||||
}],
|
||||
d: 4,
|
||||
__pad4: [0; {
|
||||
size_of::<__MultiField__>() - offset_of!(__MultiField__, d) - size_of::<u64>()
|
||||
}],
|
||||
};
|
||||
|
||||
assert_eq!(multi.a, 1);
|
||||
assert_eq!(multi.b, 2);
|
||||
assert_eq!(multi.c, 3);
|
||||
assert_eq!(multi.d, 4);
|
||||
}
|
||||
|
||||
/// Test struct with field documentation
|
||||
#[test]
|
||||
fn with_field_docs() {
|
||||
#[repr(C)]
|
||||
#[padding_struct]
|
||||
struct Documented {
|
||||
/// First field
|
||||
first: u8,
|
||||
/// Second field
|
||||
second: u32,
|
||||
}
|
||||
|
||||
let doc = Documented {
|
||||
first: 10,
|
||||
__pad1: [0; {
|
||||
offset_of!(__Documented__, second) - offset_of!(__Documented__, first) - size_of::<u8>()
|
||||
}],
|
||||
second: 20,
|
||||
__pad2: [0; {
|
||||
size_of::<__Documented__>() - offset_of!(__Documented__, second) - size_of::<u32>()
|
||||
}],
|
||||
};
|
||||
|
||||
assert_eq!(doc.first, 10);
|
||||
assert_eq!(doc.second, 20);
|
||||
}
|
||||
|
||||
/// Verify that field offsets are consistent between reference and padded structs
|
||||
#[test]
|
||||
fn offset_consistency() {
|
||||
#[repr(C)]
|
||||
#[padding_struct]
|
||||
struct OffsetTest {
|
||||
a: u8,
|
||||
b: u32,
|
||||
}
|
||||
|
||||
// Reference struct offsets
|
||||
let ref_a_offset = offset_of!(__OffsetTest__, a);
|
||||
let ref_b_offset = offset_of!(__OffsetTest__, b);
|
||||
|
||||
// Padded struct offsets should be the same
|
||||
let padded_a_offset = offset_of!(OffsetTest, a);
|
||||
let padded_b_offset = offset_of!(OffsetTest, b);
|
||||
|
||||
assert_eq!(ref_a_offset, padded_a_offset);
|
||||
assert_eq!(ref_b_offset, padded_b_offset);
|
||||
}
|
||||
|
||||
/// Test that padding is zero-filled
|
||||
#[test]
|
||||
fn padding_zero_filled() {
|
||||
#[repr(C)]
|
||||
#[padding_struct]
|
||||
struct ZeroPadded {
|
||||
a: u8,
|
||||
b: u32,
|
||||
}
|
||||
|
||||
let zero_pad = ZeroPadded {
|
||||
a: 255,
|
||||
__pad1: [0; {
|
||||
offset_of!(__ZeroPadded__, b) - offset_of!(__ZeroPadded__, a) - size_of::<u8>()
|
||||
}],
|
||||
b: 0xFFFFFFFF,
|
||||
__pad2: [0; {
|
||||
size_of::<__ZeroPadded__>() - offset_of!(__ZeroPadded__, b) - size_of::<u32>()
|
||||
}],
|
||||
};
|
||||
|
||||
// Verify padding is all zeros
|
||||
for byte in &zero_pad.__pad1 {
|
||||
assert_eq!(*byte, 0);
|
||||
}
|
||||
for byte in &zero_pad.__pad2 {
|
||||
assert_eq!(*byte, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/// Test that padded struct can derive zerocopy traits
|
||||
#[test]
|
||||
fn zerocopy_derive() {
|
||||
use zerocopy::*;
|
||||
|
||||
#[repr(C)]
|
||||
#[padding_struct]
|
||||
#[derive(Clone, Copy, FromBytes, IntoBytes, Immutable, KnownLayout)]
|
||||
struct ZerocopyStruct {
|
||||
a: u8,
|
||||
b: u32,
|
||||
c: u16,
|
||||
}
|
||||
|
||||
// Test Zeroable
|
||||
let zeroed = ZerocopyStruct::new_zeroed();
|
||||
assert_eq!(zeroed.a, 0);
|
||||
assert_eq!(zeroed.b, 0);
|
||||
assert_eq!(zeroed.c, 0);
|
||||
|
||||
// Test Pod - cast from bytes
|
||||
let bytes = [1u8, 0, 0, 0, 0x12, 0x34, 0x56, 0x78, 0xAB, 0xCD, 0, 0];
|
||||
let from_bytes: &ZerocopyStruct =
|
||||
FromBytes::ref_from_bytes(&bytes[..size_of::<ZerocopyStruct>()]).unwrap();
|
||||
assert_eq!(from_bytes.a, 1);
|
||||
assert_eq!(from_bytes.b, 0x78563412);
|
||||
assert_eq!(from_bytes.c, 0xCDAB);
|
||||
|
||||
// Test Pod - cast to bytes
|
||||
let test_struct = ZerocopyStruct {
|
||||
a: 42,
|
||||
__pad1: [0; {
|
||||
offset_of!(__ZerocopyStruct__, b) - offset_of!(__ZerocopyStruct__, a) - size_of::<u8>()
|
||||
}],
|
||||
b: 0xDEADBEEF,
|
||||
__pad2: [0; {
|
||||
offset_of!(__ZerocopyStruct__, c) - offset_of!(__ZerocopyStruct__, b) - size_of::<u32>()
|
||||
}],
|
||||
c: 0x1234,
|
||||
__pad3: [0; {
|
||||
size_of::<__ZerocopyStruct__>() - offset_of!(__ZerocopyStruct__, c) - size_of::<u16>()
|
||||
}],
|
||||
};
|
||||
let as_bytes: &[u8] = test_struct.as_bytes();
|
||||
assert_eq!(as_bytes[0], 42);
|
||||
}
|
||||
|
||||
/// Test that repr attributes (align, packed, etc.) are preserved in ref struct
|
||||
#[test]
|
||||
fn repr_align_preserved() {
|
||||
#[repr(C, align(16))]
|
||||
#[padding_struct]
|
||||
struct AlignedStruct {
|
||||
a: u8,
|
||||
b: u32,
|
||||
}
|
||||
|
||||
// Verify alignment is correct
|
||||
assert_eq!(align_of::<AlignedStruct>(), 16);
|
||||
assert_eq!(align_of::<__AlignedStruct__>(), 16);
|
||||
|
||||
let aligned = AlignedStruct {
|
||||
a: 1,
|
||||
__pad1: [0; {
|
||||
offset_of!(__AlignedStruct__, b) - offset_of!(__AlignedStruct__, a) - size_of::<u8>()
|
||||
}],
|
||||
b: 2,
|
||||
__pad2: [0; {
|
||||
size_of::<__AlignedStruct__>() - offset_of!(__AlignedStruct__, b) - size_of::<u32>()
|
||||
}],
|
||||
};
|
||||
|
||||
assert_eq!(aligned.a, 1);
|
||||
assert_eq!(aligned.b, 2);
|
||||
}
|
||||
|
||||
/// Test that size and alignment match between ref struct and padded struct
|
||||
#[test]
|
||||
fn size_align_match() {
|
||||
#[repr(C, align(8))]
|
||||
#[padding_struct]
|
||||
struct TestStruct {
|
||||
a: u8,
|
||||
b: u16,
|
||||
c: u32,
|
||||
}
|
||||
|
||||
// The compile-time check ensures these are equal
|
||||
assert_eq!(size_of::<TestStruct>(), size_of::<__TestStruct__>());
|
||||
assert_eq!(align_of::<TestStruct>(), align_of::<__TestStruct__>());
|
||||
}
|
||||
Loading…
Reference in New Issue