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