|
9 | 9 | mod generated_tests {
|
10 | 10 | #![allow(non_snake_case)]
|
11 | 11 | #![deny(improper_ctypes_definitions)]
|
12 |
| - use std::ffi::{CStr, c_char}; |
| 12 | + #[allow(unused_imports)] |
| 13 | + use std::ffi::{CStr, c_int, c_char}; |
13 | 14 | use std::fmt::{Debug, LowerHex};
|
14 | 15 | use std::sync::atomic::{AtomicBool, AtomicUsize, Ordering};
|
15 | 16 | #[allow(unused_imports)]
|
@@ -189,6 +190,129 @@ mod generated_tests {
|
189 | 190 | check_same(field_ptr.cast(), ctest_field_ptr,
|
190 | 191 | "field type {{ item.field.ident() }} of {{ item.id }}");
|
191 | 192 | }
|
| 193 | + |
| 194 | +{%- endfor +%} |
| 195 | + |
| 196 | +{%- for item in ctx.roundtrip_tests +%} |
| 197 | + |
| 198 | + /// Generates a padding map for a specific type. |
| 199 | + /// |
| 200 | + /// Essentially, it returns a list of bytes, whose length is equal to the size of the type in |
| 201 | + /// bytes. Each element corresponds to a byte and has two values. `true` if the byte is padding, |
| 202 | + /// and `false` if the byte is not padding. |
| 203 | + /// |
| 204 | + /// For aliases we assume that there are no padding bytes, for structs and unions, |
| 205 | + /// if there are no fields, then everything is padding, if there are fields, then we have to |
| 206 | + /// go through each field and figure out the padding. |
| 207 | + fn roundtrip_padding__{{ item.id }}() -> Vec<bool> { |
| 208 | + if {{ item.fields.len() }} == 0 { |
| 209 | + // FIXME(ctest): What if it's an alias to a struct/union? |
| 210 | + return vec![!{{ item.is_alias }}; size_of::<{{ item.id }}>()] |
| 211 | + } |
| 212 | + |
| 213 | + // If there are no fields, v and bar become unused. |
| 214 | + #[allow(unused_mut)] |
| 215 | + let mut v = Vec::<(usize, usize)>::new(); |
| 216 | + #[allow(unused_variables)] |
| 217 | + let bar = MaybeUninit::<{{ item.id }}>::zeroed(); |
| 218 | + #[allow(unused_variables)] |
| 219 | + let bar = bar.as_ptr(); |
| 220 | + {%- for field in item.fields +%} |
| 221 | + |
| 222 | + let ty_ptr = unsafe { &raw const ((*bar).{{ field.ident() }}) }; |
| 223 | + let val = unsafe { ty_ptr.read_unaligned() }; |
| 224 | + |
| 225 | + let size = size_of_val(&val); |
| 226 | + let off = offset_of!({{ item.id }}, {{ field.ident() }}); |
| 227 | + v.push((off, size)); |
| 228 | + {%- endfor +%} |
| 229 | + // This vector contains `true` if the byte is padding and `false` if the byte is not |
| 230 | + // padding. Initialize all bytes as: |
| 231 | + // - padding if we have fields, this means that only the fields will be checked |
| 232 | + // - no-padding if we have a type alias: if this causes problems the type alias should |
| 233 | + // be skipped |
| 234 | + let mut is_padding_byte = vec![true; size_of::<{{ item.id }}>()]; |
| 235 | + for (off, size) in &v { |
| 236 | + for i in 0..*size { |
| 237 | + is_padding_byte[off + i] = false; |
| 238 | + } |
| 239 | + } |
| 240 | + is_padding_byte |
| 241 | + } |
| 242 | + |
| 243 | + /// Tests whether a type alias when passed to C and back to Rust remains unchanged. |
| 244 | + /// |
| 245 | + /// It checks if the size is the same as well as if the padding bytes are all in the |
| 246 | + /// correct place. For this test to be sound, `T` must be valid for any bitpattern. |
| 247 | + pub fn {{ item.test_name }}() { |
| 248 | + type U = {{ item.id }}; |
| 249 | + extern "C" { |
| 250 | + fn ctest_size_of__{{ item.id }}() -> u64; |
| 251 | + fn ctest_roundtrip__{{ item.id }}( |
| 252 | + input: MaybeUninit<U>, is_padding_byte: *const bool, value_bytes: *mut u8 |
| 253 | + ) -> U; |
| 254 | + } |
| 255 | + |
| 256 | + const SIZE: usize = size_of::<U>(); |
| 257 | + |
| 258 | + let is_padding_byte = roundtrip_padding__{{ item.id }}(); |
| 259 | + let mut expected = vec![0u8; SIZE]; |
| 260 | + let mut input = MaybeUninit::<U>::zeroed(); |
| 261 | + |
| 262 | + let input_ptr = input.as_mut_ptr().cast::<u8>(); |
| 263 | + |
| 264 | + // Fill the unitialized memory with a deterministic pattern. |
| 265 | + // From Rust to C: every byte will be labelled from 1 to 255, with 0 turning into 42. |
| 266 | + // From C to Rust: every byte will be inverted from before (254 -> 1), but 0 is still 42. |
| 267 | + for i in 0..SIZE { |
| 268 | + let c: u8 = (i % 256) as u8; |
| 269 | + let c = if c == 0 { 42 } else { c }; |
| 270 | + let d: u8 = 255_u8 - (i % 256) as u8; |
| 271 | + let d = if d == 0 { 42 } else { d }; |
| 272 | + unsafe { |
| 273 | + input_ptr.add(i).write_volatile(c); |
| 274 | + expected[i] = d; |
| 275 | + } |
| 276 | + } |
| 277 | + |
| 278 | + let c_size = unsafe { ctest_size_of__{{ item.id }}() } as usize; |
| 279 | + if SIZE != c_size { |
| 280 | + FAILED.store(true, Ordering::Relaxed); |
| 281 | + eprintln!( |
| 282 | + "size of {{ item.c_ty }} is {c_size} in C and {SIZE} in Rust\n", |
| 283 | + ); |
| 284 | + return; |
| 285 | + } |
| 286 | + |
| 287 | + let mut c_value_bytes = vec![0; size_of::<{{ item.id }}>()]; |
| 288 | + let r: U = unsafe { |
| 289 | + ctest_roundtrip__{{ item.id }}(input, is_padding_byte.as_ptr(), c_value_bytes.as_mut_ptr()) |
| 290 | + }; |
| 291 | + |
| 292 | + // Check that the value bytes as read from C match the byte we sent from Rust. |
| 293 | + for (i, is_padding_byte) in is_padding_byte.iter().enumerate() { |
| 294 | + if *is_padding_byte { continue; } |
| 295 | + let rust = unsafe { *input_ptr.add(i) }; |
| 296 | + let c = c_value_bytes[i]; |
| 297 | + if rust != c { |
| 298 | + eprintln!("rust[{}] = {} != {} (C): Rust \"{{ item.id }}\" -> C", i, rust, c); |
| 299 | + FAILED.store(true, Ordering::Relaxed); |
| 300 | + } |
| 301 | + } |
| 302 | + |
| 303 | + // Check that value returned from C contains the bytes we expect. |
| 304 | + for (i, is_padding_byte) in is_padding_byte.iter().enumerate() { |
| 305 | + if *is_padding_byte { continue; } |
| 306 | + let rust = expected[i] as usize; |
| 307 | + let c = unsafe { (&raw const r).cast::<u8>().add(i).read_volatile() as usize }; |
| 308 | + if rust != c { |
| 309 | + eprintln!( |
| 310 | + "rust [{i}] = {rust} != {c} (C): C \"{{ item.id }}\" -> Rust", |
| 311 | + ); |
| 312 | + FAILED.store(true, Ordering::Relaxed); |
| 313 | + } |
| 314 | + } |
| 315 | + } |
192 | 316 | {%- endfor +%}
|
193 | 317 | }
|
194 | 318 |
|
|
0 commit comments