1 // Copyright (C) 2025 Intel Corporation. 2 // Author(s): Zhao Liu <zhao1.liu@intel.com> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 use std::{ 6 ffi::{c_void, CStr}, 7 mem::size_of, 8 ptr::NonNull, 9 slice, 10 }; 11 12 use qemu_api::{ 13 bindings::{ 14 vmstate_info_bool, vmstate_info_int32, vmstate_info_int64, vmstate_info_int8, 15 vmstate_info_uint64, vmstate_info_uint8, vmstate_info_unused_buffer, VMStateFlags, 16 }, 17 cell::{BqlCell, Opaque}, 18 impl_vmstate_forward, 19 vmstate::{VMStateDescription, VMStateField}, 20 vmstate_fields, vmstate_of, vmstate_struct, vmstate_unused, vmstate_validate, 21 zeroable::Zeroable, 22 }; 23 24 const FOO_ARRAY_MAX: usize = 3; 25 26 // =========================== Test VMSTATE_FOOA =========================== 27 // Test the use cases of the vmstate macro, corresponding to the following C 28 // macro variants: 29 // * VMSTATE_FOOA: 30 // - VMSTATE_U16 31 // - VMSTATE_UNUSED 32 // - VMSTATE_VARRAY_UINT16_UNSAFE 33 // - VMSTATE_VARRAY_MULTIPLY 34 #[repr(C)] 35 #[derive(Default)] 36 struct FooA { 37 arr: [u8; FOO_ARRAY_MAX], 38 num: u16, 39 arr_mul: [i8; FOO_ARRAY_MAX], 40 num_mul: u32, 41 elem: i8, 42 } 43 44 static VMSTATE_FOOA: VMStateDescription = VMStateDescription { 45 name: c"foo_a".as_ptr(), 46 version_id: 1, 47 minimum_version_id: 1, 48 fields: vmstate_fields! { 49 vmstate_of!(FooA, elem), 50 vmstate_unused!(size_of::<i64>()), 51 vmstate_of!(FooA, arr[0 .. num]).with_version_id(0), 52 vmstate_of!(FooA, arr_mul[0 .. num_mul * 16]), 53 }, 54 ..Zeroable::ZERO 55 }; 56 57 #[test] 58 fn test_vmstate_uint16() { 59 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; 60 61 // 1st VMStateField ("elem") in VMSTATE_FOOA (corresponding to VMSTATE_UINT16) 62 assert_eq!( 63 unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), 64 b"elem\0" 65 ); 66 assert_eq!(foo_fields[0].offset, 16); 67 assert_eq!(foo_fields[0].num_offset, 0); 68 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int8 }); 69 assert_eq!(foo_fields[0].version_id, 0); 70 assert_eq!(foo_fields[0].size, 1); 71 assert_eq!(foo_fields[0].num, 0); 72 assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); 73 assert!(foo_fields[0].vmsd.is_null()); 74 assert!(foo_fields[0].field_exists.is_none()); 75 } 76 77 #[test] 78 fn test_vmstate_unused() { 79 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; 80 81 // 2nd VMStateField ("unused") in VMSTATE_FOOA (corresponding to VMSTATE_UNUSED) 82 assert_eq!( 83 unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), 84 b"unused\0" 85 ); 86 assert_eq!(foo_fields[1].offset, 0); 87 assert_eq!(foo_fields[1].num_offset, 0); 88 assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_unused_buffer }); 89 assert_eq!(foo_fields[1].version_id, 0); 90 assert_eq!(foo_fields[1].size, 8); 91 assert_eq!(foo_fields[1].num, 0); 92 assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_BUFFER); 93 assert!(foo_fields[1].vmsd.is_null()); 94 assert!(foo_fields[1].field_exists.is_none()); 95 } 96 97 #[test] 98 fn test_vmstate_varray_uint16_unsafe() { 99 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; 100 101 // 3rd VMStateField ("arr") in VMSTATE_FOOA (corresponding to 102 // VMSTATE_VARRAY_UINT16_UNSAFE) 103 assert_eq!( 104 unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), 105 b"arr\0" 106 ); 107 assert_eq!(foo_fields[2].offset, 0); 108 assert_eq!(foo_fields[2].num_offset, 4); 109 assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); 110 assert_eq!(foo_fields[2].version_id, 0); 111 assert_eq!(foo_fields[2].size, 1); 112 assert_eq!(foo_fields[2].num, 0); 113 assert_eq!(foo_fields[2].flags, VMStateFlags::VMS_VARRAY_UINT16); 114 assert!(foo_fields[2].vmsd.is_null()); 115 assert!(foo_fields[2].field_exists.is_none()); 116 } 117 118 #[test] 119 fn test_vmstate_varray_multiply() { 120 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOA.fields, 5) }; 121 122 // 4th VMStateField ("arr_mul") in VMSTATE_FOOA (corresponding to 123 // VMSTATE_VARRAY_MULTIPLY) 124 assert_eq!( 125 unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), 126 b"arr_mul\0" 127 ); 128 assert_eq!(foo_fields[3].offset, 6); 129 assert_eq!(foo_fields[3].num_offset, 12); 130 assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_int8 }); 131 assert_eq!(foo_fields[3].version_id, 0); 132 assert_eq!(foo_fields[3].size, 1); 133 assert_eq!(foo_fields[3].num, 16); 134 assert_eq!( 135 foo_fields[3].flags.0, 136 VMStateFlags::VMS_VARRAY_UINT32.0 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 137 ); 138 assert!(foo_fields[3].vmsd.is_null()); 139 assert!(foo_fields[3].field_exists.is_none()); 140 141 // The last VMStateField in VMSTATE_FOOA. 142 assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); 143 } 144 145 // =========================== Test VMSTATE_FOOB =========================== 146 // Test the use cases of the vmstate macro, corresponding to the following C 147 // macro variants: 148 // * VMSTATE_FOOB: 149 // - VMSTATE_BOOL_V 150 // - VMSTATE_U64 151 // - VMSTATE_STRUCT_VARRAY_UINT8 152 // - (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32 153 // - VMSTATE_ARRAY 154 // - VMSTATE_STRUCT_VARRAY_UINT8 with BqlCell wrapper & test_fn 155 #[repr(C)] 156 #[derive(Default)] 157 struct FooB { 158 arr_a: [FooA; FOO_ARRAY_MAX], 159 num_a: u8, 160 arr_a_mul: [FooA; FOO_ARRAY_MAX], 161 num_a_mul: u32, 162 wrap: BqlCell<u64>, 163 val: bool, 164 // FIXME: Use Timer array. Now we can't since it's hard to link savevm.c to test. 165 arr_i64: [i64; FOO_ARRAY_MAX], 166 arr_a_wrap: [FooA; FOO_ARRAY_MAX], 167 num_a_wrap: BqlCell<u32>, 168 } 169 170 fn validate_foob(_state: &FooB, _version_id: u8) -> bool { 171 true 172 } 173 174 static VMSTATE_FOOB: VMStateDescription = VMStateDescription { 175 name: c"foo_b".as_ptr(), 176 version_id: 2, 177 minimum_version_id: 1, 178 fields: vmstate_fields! { 179 vmstate_of!(FooB, val).with_version_id(2), 180 vmstate_of!(FooB, wrap), 181 vmstate_struct!(FooB, arr_a[0 .. num_a], &VMSTATE_FOOA, FooA).with_version_id(1), 182 vmstate_struct!(FooB, arr_a_mul[0 .. num_a_mul * 32], &VMSTATE_FOOA, FooA).with_version_id(2), 183 vmstate_of!(FooB, arr_i64), 184 vmstate_struct!(FooB, arr_a_wrap[0 .. num_a_wrap], &VMSTATE_FOOA, FooA, validate_foob), 185 }, 186 ..Zeroable::ZERO 187 }; 188 189 #[test] 190 fn test_vmstate_bool_v() { 191 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; 192 193 // 1st VMStateField ("val") in VMSTATE_FOOB (corresponding to VMSTATE_BOOL_V) 194 assert_eq!( 195 unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), 196 b"val\0" 197 ); 198 assert_eq!(foo_fields[0].offset, 136); 199 assert_eq!(foo_fields[0].num_offset, 0); 200 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_bool }); 201 assert_eq!(foo_fields[0].version_id, 2); 202 assert_eq!(foo_fields[0].size, 1); 203 assert_eq!(foo_fields[0].num, 0); 204 assert_eq!(foo_fields[0].flags, VMStateFlags::VMS_SINGLE); 205 assert!(foo_fields[0].vmsd.is_null()); 206 assert!(foo_fields[0].field_exists.is_none()); 207 } 208 209 #[test] 210 fn test_vmstate_uint64() { 211 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; 212 213 // 2nd VMStateField ("wrap") in VMSTATE_FOOB (corresponding to VMSTATE_U64) 214 assert_eq!( 215 unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), 216 b"wrap\0" 217 ); 218 assert_eq!(foo_fields[1].offset, 128); 219 assert_eq!(foo_fields[1].num_offset, 0); 220 assert_eq!(foo_fields[1].info, unsafe { &vmstate_info_uint64 }); 221 assert_eq!(foo_fields[1].version_id, 0); 222 assert_eq!(foo_fields[1].size, 8); 223 assert_eq!(foo_fields[1].num, 0); 224 assert_eq!(foo_fields[1].flags, VMStateFlags::VMS_SINGLE); 225 assert!(foo_fields[1].vmsd.is_null()); 226 assert!(foo_fields[1].field_exists.is_none()); 227 } 228 229 #[test] 230 fn test_vmstate_struct_varray_uint8() { 231 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; 232 233 // 3rd VMStateField ("arr_a") in VMSTATE_FOOB (corresponding to 234 // VMSTATE_STRUCT_VARRAY_UINT8) 235 assert_eq!( 236 unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), 237 b"arr_a\0" 238 ); 239 assert_eq!(foo_fields[2].offset, 0); 240 assert_eq!(foo_fields[2].num_offset, 60); 241 assert!(foo_fields[2].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. 242 assert_eq!(foo_fields[2].version_id, 1); 243 assert_eq!(foo_fields[2].size, 20); 244 assert_eq!(foo_fields[2].num, 0); 245 assert_eq!( 246 foo_fields[2].flags.0, 247 VMStateFlags::VMS_STRUCT.0 | VMStateFlags::VMS_VARRAY_UINT8.0 248 ); 249 assert_eq!(foo_fields[2].vmsd, &VMSTATE_FOOA); 250 assert!(foo_fields[2].field_exists.is_none()); 251 } 252 253 #[test] 254 fn test_vmstate_struct_varray_uint32_multiply() { 255 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; 256 257 // 4th VMStateField ("arr_a_mul") in VMSTATE_FOOB (corresponding to 258 // (no C version) MULTIPLY variant of VMSTATE_STRUCT_VARRAY_UINT32) 259 assert_eq!( 260 unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), 261 b"arr_a_mul\0" 262 ); 263 assert_eq!(foo_fields[3].offset, 64); 264 assert_eq!(foo_fields[3].num_offset, 124); 265 assert!(foo_fields[3].info.is_null()); // VMSTATE_STRUCT_VARRAY_UINT8 doesn't set info field. 266 assert_eq!(foo_fields[3].version_id, 2); 267 assert_eq!(foo_fields[3].size, 20); 268 assert_eq!(foo_fields[3].num, 32); 269 assert_eq!( 270 foo_fields[3].flags.0, 271 VMStateFlags::VMS_STRUCT.0 272 | VMStateFlags::VMS_VARRAY_UINT32.0 273 | VMStateFlags::VMS_MULTIPLY_ELEMENTS.0 274 ); 275 assert_eq!(foo_fields[3].vmsd, &VMSTATE_FOOA); 276 assert!(foo_fields[3].field_exists.is_none()); 277 } 278 279 #[test] 280 fn test_vmstate_macro_array() { 281 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; 282 283 // 5th VMStateField ("arr_i64") in VMSTATE_FOOB (corresponding to 284 // VMSTATE_ARRAY) 285 assert_eq!( 286 unsafe { CStr::from_ptr(foo_fields[4].name) }.to_bytes_with_nul(), 287 b"arr_i64\0" 288 ); 289 assert_eq!(foo_fields[4].offset, 144); 290 assert_eq!(foo_fields[4].num_offset, 0); 291 assert_eq!(foo_fields[4].info, unsafe { &vmstate_info_int64 }); 292 assert_eq!(foo_fields[4].version_id, 0); 293 assert_eq!(foo_fields[4].size, 8); 294 assert_eq!(foo_fields[4].num, FOO_ARRAY_MAX as i32); 295 assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_ARRAY); 296 assert!(foo_fields[4].vmsd.is_null()); 297 assert!(foo_fields[4].field_exists.is_none()); 298 } 299 300 #[test] 301 fn test_vmstate_struct_varray_uint8_wrapper() { 302 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOB.fields, 7) }; 303 let mut foo_b: FooB = Default::default(); 304 let foo_b_p = std::ptr::addr_of_mut!(foo_b).cast::<c_void>(); 305 306 // 6th VMStateField ("arr_a_wrap") in VMSTATE_FOOB (corresponding to 307 // VMSTATE_STRUCT_VARRAY_UINT8). Other fields are checked in 308 // test_vmstate_struct_varray_uint8. 309 assert_eq!( 310 unsafe { CStr::from_ptr(foo_fields[5].name) }.to_bytes_with_nul(), 311 b"arr_a_wrap\0" 312 ); 313 assert_eq!(foo_fields[5].num_offset, 228); 314 assert!(unsafe { foo_fields[5].field_exists.unwrap()(foo_b_p, 0) }); 315 316 // The last VMStateField in VMSTATE_FOOB. 317 assert_eq!(foo_fields[6].flags, VMStateFlags::VMS_END); 318 } 319 320 // =========================== Test VMSTATE_FOOC =========================== 321 // Test the use cases of the vmstate macro, corresponding to the following C 322 // macro variants: 323 // * VMSTATE_FOOC: 324 // - VMSTATE_POINTER 325 // - VMSTATE_ARRAY_OF_POINTER 326 struct FooCWrapper([Opaque<*mut u8>; FOO_ARRAY_MAX]); // Though Opaque<> array is almost impossible. 327 328 impl_vmstate_forward!(FooCWrapper); 329 330 #[repr(C)] 331 struct FooC { 332 ptr: *const i32, 333 ptr_a: NonNull<FooA>, 334 arr_ptr: [Box<u8>; FOO_ARRAY_MAX], 335 arr_ptr_wrap: FooCWrapper, 336 } 337 338 static VMSTATE_FOOC: VMStateDescription = VMStateDescription { 339 name: c"foo_c".as_ptr(), 340 version_id: 3, 341 minimum_version_id: 1, 342 fields: vmstate_fields! { 343 vmstate_of!(FooC, ptr).with_version_id(2), 344 // FIXME: Currently vmstate_struct doesn't support the pointer to structure. 345 // VMSTATE_STRUCT_POINTER: vmstate_struct!(FooC, ptr_a, VMSTATE_FOOA, NonNull<FooA>) 346 vmstate_unused!(size_of::<NonNull<FooA>>()), 347 vmstate_of!(FooC, arr_ptr), 348 vmstate_of!(FooC, arr_ptr_wrap), 349 }, 350 ..Zeroable::ZERO 351 }; 352 353 const PTR_SIZE: usize = size_of::<*mut ()>(); 354 355 #[test] 356 fn test_vmstate_pointer() { 357 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; 358 359 // 1st VMStateField ("ptr") in VMSTATE_FOOC (corresponding to VMSTATE_POINTER) 360 assert_eq!( 361 unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), 362 b"ptr\0" 363 ); 364 assert_eq!(foo_fields[0].offset, 0); 365 assert_eq!(foo_fields[0].num_offset, 0); 366 assert_eq!(foo_fields[0].info, unsafe { &vmstate_info_int32 }); 367 assert_eq!(foo_fields[0].version_id, 2); 368 assert_eq!(foo_fields[0].size, 4); 369 assert_eq!(foo_fields[0].num, 0); 370 assert_eq!( 371 foo_fields[0].flags.0, 372 VMStateFlags::VMS_SINGLE.0 | VMStateFlags::VMS_POINTER.0 373 ); 374 assert!(foo_fields[0].vmsd.is_null()); 375 assert!(foo_fields[0].field_exists.is_none()); 376 } 377 378 #[test] 379 fn test_vmstate_macro_array_of_pointer() { 380 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; 381 382 // 3rd VMStateField ("arr_ptr") in VMSTATE_FOOC (corresponding to 383 // VMSTATE_ARRAY_OF_POINTER) 384 assert_eq!( 385 unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), 386 b"arr_ptr\0" 387 ); 388 assert_eq!(foo_fields[2].offset, 2 * PTR_SIZE); 389 assert_eq!(foo_fields[2].num_offset, 0); 390 assert_eq!(foo_fields[2].info, unsafe { &vmstate_info_uint8 }); 391 assert_eq!(foo_fields[2].version_id, 0); 392 assert_eq!(foo_fields[2].size, PTR_SIZE); 393 assert_eq!(foo_fields[2].num, FOO_ARRAY_MAX as i32); 394 assert_eq!( 395 foo_fields[2].flags.0, 396 VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 397 ); 398 assert!(foo_fields[2].vmsd.is_null()); 399 assert!(foo_fields[2].field_exists.is_none()); 400 } 401 402 #[test] 403 fn test_vmstate_macro_array_of_pointer_wrapped() { 404 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOC.fields, 6) }; 405 406 // 4th VMStateField ("arr_ptr_wrap") in VMSTATE_FOOC (corresponding to 407 // VMSTATE_ARRAY_OF_POINTER) 408 assert_eq!( 409 unsafe { CStr::from_ptr(foo_fields[3].name) }.to_bytes_with_nul(), 410 b"arr_ptr_wrap\0" 411 ); 412 assert_eq!(foo_fields[3].offset, (FOO_ARRAY_MAX + 2) * PTR_SIZE); 413 assert_eq!(foo_fields[3].num_offset, 0); 414 assert_eq!(foo_fields[3].info, unsafe { &vmstate_info_uint8 }); 415 assert_eq!(foo_fields[3].version_id, 0); 416 assert_eq!(foo_fields[3].size, PTR_SIZE); 417 assert_eq!(foo_fields[3].num, FOO_ARRAY_MAX as i32); 418 assert_eq!( 419 foo_fields[3].flags.0, 420 VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_ARRAY_OF_POINTER.0 421 ); 422 assert!(foo_fields[3].vmsd.is_null()); 423 assert!(foo_fields[3].field_exists.is_none()); 424 425 // The last VMStateField in VMSTATE_FOOC. 426 assert_eq!(foo_fields[4].flags, VMStateFlags::VMS_END); 427 } 428 429 // =========================== Test VMSTATE_FOOD =========================== 430 // Test the use cases of the vmstate macro, corresponding to the following C 431 // macro variants: 432 // * VMSTATE_FOOD: 433 // - VMSTATE_VALIDATE 434 435 // Add more member fields when vmstate_of/vmstate_struct support "test" 436 // parameter. 437 struct FooD; 438 439 impl FooD { 440 fn validate_food_0(&self, _version_id: u8) -> bool { 441 true 442 } 443 444 fn validate_food_1(_state: &FooD, _version_id: u8) -> bool { 445 false 446 } 447 } 448 449 fn validate_food_2(_state: &FooD, _version_id: u8) -> bool { 450 true 451 } 452 453 static VMSTATE_FOOD: VMStateDescription = VMStateDescription { 454 name: c"foo_d".as_ptr(), 455 version_id: 3, 456 minimum_version_id: 1, 457 fields: vmstate_fields! { 458 vmstate_validate!(FooD, c"foo_d_0", FooD::validate_food_0), 459 vmstate_validate!(FooD, c"foo_d_1", FooD::validate_food_1), 460 vmstate_validate!(FooD, c"foo_d_2", validate_food_2), 461 }, 462 ..Zeroable::ZERO 463 }; 464 465 #[test] 466 fn test_vmstate_validate() { 467 let foo_fields: &[VMStateField] = unsafe { slice::from_raw_parts(VMSTATE_FOOD.fields, 4) }; 468 let mut foo_d = FooD; 469 let foo_d_p = std::ptr::addr_of_mut!(foo_d).cast::<c_void>(); 470 471 // 1st VMStateField in VMSTATE_FOOD 472 assert_eq!( 473 unsafe { CStr::from_ptr(foo_fields[0].name) }.to_bytes_with_nul(), 474 b"foo_d_0\0" 475 ); 476 assert_eq!(foo_fields[0].offset, 0); 477 assert_eq!(foo_fields[0].num_offset, 0); 478 assert!(foo_fields[0].info.is_null()); 479 assert_eq!(foo_fields[0].version_id, 0); 480 assert_eq!(foo_fields[0].size, 0); 481 assert_eq!(foo_fields[0].num, 0); 482 assert_eq!( 483 foo_fields[0].flags.0, 484 VMStateFlags::VMS_ARRAY.0 | VMStateFlags::VMS_MUST_EXIST.0 485 ); 486 assert!(foo_fields[0].vmsd.is_null()); 487 assert!(unsafe { foo_fields[0].field_exists.unwrap()(foo_d_p, 0) }); 488 489 // 2nd VMStateField in VMSTATE_FOOD 490 assert_eq!( 491 unsafe { CStr::from_ptr(foo_fields[1].name) }.to_bytes_with_nul(), 492 b"foo_d_1\0" 493 ); 494 assert!(!unsafe { foo_fields[1].field_exists.unwrap()(foo_d_p, 1) }); 495 496 // 3rd VMStateField in VMSTATE_FOOD 497 assert_eq!( 498 unsafe { CStr::from_ptr(foo_fields[2].name) }.to_bytes_with_nul(), 499 b"foo_d_2\0" 500 ); 501 assert!(unsafe { foo_fields[2].field_exists.unwrap()(foo_d_p, 2) }); 502 503 // The last VMStateField in VMSTATE_FOOD. 504 assert_eq!(foo_fields[3].flags, VMStateFlags::VMS_END); 505 } 506