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