forked from qemu/qemu
-
Notifications
You must be signed in to change notification settings - Fork 7
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
rust: introduce alternative implementation of offset_of!
offset_of! was stabilized in Rust 1.77.0. Use an alternative implemenation that was found on the Rust forums, and whose author agreed to license as MIT for use in QEMU. The alternative allows only one level of field access, but apart from this can be used just by replacing core::mem::offset_of! with qemu_api::offset_of!. The actual implementation of offset_of! is done in a declarative macro, but for simplicity and to avoid introducing an extra level of indentation, the trigger is a procedural macro #[derive(offsets)]. The procedural macro is perhaps a bit overengineered, but it helps introducing some idioms that will be useful in the future as well. Signed-off-by: Junjie Mao <[email protected]> Co-developed-by: Paolo Bonzini <[email protected]> Signed-off-by: Paolo Bonzini <[email protected]>
- Loading branch information
1 parent
39c8fae
commit f351840
Showing
13 changed files
with
274 additions
and
18 deletions.
There are no files selected for viewing
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
// SPDX-License-Identifier: MIT | ||
|
||
/// This macro provides the same functionality as `core::mem::offset_of`, | ||
/// except that only one level of field access is supported. The declaration | ||
/// of the struct must be wrapped with `with_offsets! { }`. | ||
/// | ||
/// It is needed because `offset_of!` was only stabilized in Rust 1.77. | ||
#[cfg(not(has_offset_of))] | ||
#[macro_export] | ||
macro_rules! offset_of { | ||
($Container:ty, $field:ident) => { | ||
<$Container>::OFFSET_TO__.$field | ||
}; | ||
} | ||
|
||
/// A wrapper for struct declarations, that allows using `offset_of!` in | ||
/// versions of Rust prior to 1.77 | ||
#[macro_export] | ||
macro_rules! with_offsets { | ||
// This method to generate field offset constants comes from: | ||
// | ||
// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=10a22a9b8393abd7b541d8fc844bc0df | ||
// | ||
// used under MIT license with permission of Yandros aka Daniel Henry-Mantilla | ||
( | ||
$(#[$struct_meta:meta])* | ||
$struct_vis:vis | ||
struct $StructName:ident { | ||
$( | ||
$(#[$field_meta:meta])* | ||
$field_vis:vis | ||
$field_name:ident : $field_ty:ty | ||
),* | ||
$(,)? | ||
} | ||
) => ( | ||
#[cfg(not(has_offset_of))] | ||
const _: () = { | ||
struct StructOffsetsHelper<T>(std::marker::PhantomData<T>); | ||
const END_OF_PREV_FIELD: usize = 0; | ||
|
||
// populate StructOffsetsHelper<T> with associated consts, | ||
// one for each field | ||
$crate::with_offsets! { | ||
@struct $StructName | ||
@names [ $($field_name)* ] | ||
@tys [ $($field_ty ,)*] | ||
} | ||
|
||
// now turn StructOffsetsHelper<T>'s consts into a single struct, | ||
// applying field visibility. This provides better error messages | ||
// than if offset_of! used StructOffsetsHelper::<T> directly. | ||
pub | ||
struct StructOffsets { | ||
$( | ||
$field_vis | ||
$field_name: usize, | ||
)* | ||
} | ||
impl $StructName { | ||
pub | ||
const OFFSET_TO__: StructOffsets = StructOffsets { | ||
$( | ||
$field_name: StructOffsetsHelper::<$StructName>::$field_name, | ||
)* | ||
}; | ||
} | ||
}; | ||
); | ||
|
||
( | ||
@struct $StructName:ident | ||
@names [] | ||
@tys [] | ||
) => (); | ||
|
||
( | ||
@struct $StructName:ident | ||
@names [$field_name:ident $($other_names:tt)*] | ||
@tys [$field_ty:ty , $($other_tys:tt)*] | ||
) => ( | ||
#[allow(non_local_definitions)] | ||
#[allow(clippy::modulo_one)] | ||
impl StructOffsetsHelper<$StructName> { | ||
#[allow(nonstandard_style)] | ||
const $field_name: usize = { | ||
const ALIGN: usize = std::mem::align_of::<$field_ty>(); | ||
const TRAIL: usize = END_OF_PREV_FIELD % ALIGN; | ||
END_OF_PREV_FIELD + (if TRAIL == 0 { 0usize } else { ALIGN - TRAIL }) | ||
}; | ||
} | ||
const _: () = { | ||
const END_OF_PREV_FIELD: usize = | ||
StructOffsetsHelper::<$StructName>::$field_name + | ||
std::mem::size_of::<$field_ty>() | ||
; | ||
$crate::with_offsets! { | ||
@struct $StructName | ||
@names [$($other_names)*] | ||
@tys [$($other_tys)*] | ||
} | ||
}; | ||
); | ||
} | ||
|
||
#[cfg(test)] | ||
mod tests { | ||
use crate::offset_of; | ||
|
||
#[repr(C)] | ||
struct Foo { | ||
a: u16, | ||
b: u32, | ||
c: u64, | ||
d: u16, | ||
} | ||
|
||
#[repr(C)] | ||
struct Bar { | ||
pub a: u16, | ||
pub b: u64, | ||
c: Foo, | ||
d: u64, | ||
} | ||
|
||
crate::with_offsets! { | ||
#[repr(C)] | ||
struct Bar { | ||
pub a: u16, | ||
pub b: u64, | ||
c: Foo, | ||
d: u64, | ||
} | ||
} | ||
|
||
#[repr(C)] | ||
pub struct Baz { | ||
b: u32, | ||
a: u8, | ||
} | ||
crate::with_offsets! { | ||
#[repr(C)] | ||
pub struct Baz { | ||
b: u32, | ||
a: u8, | ||
} | ||
} | ||
|
||
#[test] | ||
fn test_offset_of() { | ||
const OFFSET_TO_C: usize = offset_of!(Bar, c); | ||
|
||
assert_eq!(offset_of!(Bar, a), 0); | ||
assert_eq!(offset_of!(Bar, b), 8); | ||
assert_eq!(OFFSET_TO_C, 16); | ||
assert_eq!(offset_of!(Bar, d), 40); | ||
|
||
assert_eq!(offset_of!(Baz, b), 0); | ||
assert_eq!(offset_of!(Baz, a), 4); | ||
} | ||
} |
Oops, something went wrong.