From f358e4f28f9cb5c80198df9926b51247e3263505 Mon Sep 17 00:00:00 2001 From: ljy9810 Date: Tue, 14 Oct 2025 20:21:17 +0800 Subject: [PATCH] =?UTF-8?q?memoffset=E5=8D=87=E7=BA=A7=E5=88=B00.9.1?= =?UTF-8?q?=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: ljy9810 --- .github/workflows/ci.yml | 51 ++++++------------- BUILD.gn | 2 +- CHANGELOG.md | 32 ++++++++++++ Cargo.toml | 4 +- README.OpenSource | 2 +- README.md | 26 +++------- build.rs | 6 +++ src/lib.rs | 12 ++--- src/offset_of.rs | 107 +++++++++++++++++++++++++++++---------- src/raw_field.rs | 10 ++-- src/span_of.rs | 18 +++---- 11 files changed, 163 insertions(+), 107 deletions(-) create mode 100644 CHANGELOG.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ea6b607..1cce2af 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,11 +13,10 @@ jobs: - beta - nightly steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - override: true - name: Run cargo test run: cargo test @@ -33,60 +32,40 @@ jobs: - 1.36.0 # Oldest supported with MaybeUninit - 1.40.0 # Oldest supported with cfg(doctest) - 1.51.0 # Oldest supported with ptr::addr_of! - - stable - - beta - - nightly + - 1.65.0 # Oldest supported with stable const evaluation (sans cell) + - 1.77.0 # Oldest supported with native `offset_of!` steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - override: true - name: Run cargo test # Exclude doctests here, as we don't want to clutter docs themselves # with backwards compatibility workarounds. run: cargo test --lib - nightly: - name: Test Suite (nightly features) - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 - with: - toolchain: nightly - override: true - - name: Run cargo test - # `--lib` prevents doctests from being run. - # This is due to `unstable_const` requiring extra `feature(...)` directives - # which the doctests do not have. - run: cargo test --all-features --lib - miri: name: Test Suite (Miri) runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Install Miri - run: | - rustup toolchain install nightly --component miri - rustup override set nightly - cargo miri setup + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master + with: + toolchain: nightly + components: miri - name: Test with Miri run: | cargo miri test - cargo miri test --all-features style: name: lints and formatting runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: actions-rs/toolchain@v1 + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@master with: - toolchain: 1.51.0 # pin a version for reproducible results - components: rustfmt - override: true + toolchain: 1.51.0 # pin a version for reproducible results + components: rustfmt - name: Check warnings run: RUSTFLAGS="-D warnings" cargo check --all-targets - name: Check formatting diff --git a/BUILD.gn b/BUILD.gn index 32b2e33..3e8f8aa 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -20,7 +20,7 @@ ohos_cargo_crate("lib") { sources = ["src/lib.rs"] edition = "2015" - cargo_pkg_version = "0.7.1" + cargo_pkg_version = "0.9.1" cargo_pkg_authors = "Gilad Naaman " cargo_pkg_name = "memoffset" cargo_pkg_description = "offset_of functionality for Rust structs." diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b1b0e63 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,32 @@ +# Changelog + +## Unreleased + +## v0.9.1 (26/03/2024) +### Added + - Added changelog + +### Changed + - Clarify documentation about macro indirection + - Turn the crate into a thin stdlib wrapper on rustc>=1.77 + - Turn `unstable_offset_of` and `unstable_const` into NOPs; they are not needed any more on recent nightlies + +## v0.9.0 (18/05/2023) +### Added + - Cargo feature `unstable_offset_of` which turns the crate into a stdlib polyfill + +## v0.8.0 (15/12/2022) +### Changed + - Constant-evaluation is automatically enabled + +## v0.7.1 (17/10/2022) +### Changed + - Version in `README.md` + +## v0.7.0 (17/10/2022) +### Added + - `offset_of_union!` + +## v0.6.5 (03/12/2021) +### Removed + - [nightly] `#![feature(const_raw_ptr_deref, const_maybe_uninit_as_ptr)]` diff --git a/Cargo.toml b/Cargo.toml index 90620e2..9eeaebe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "memoffset" -version = "0.7.1" +version = "0.9.1" authors = ["Gilad Naaman "] description = "offset_of functionality for Rust structs." license = "MIT" @@ -17,4 +17,6 @@ doc-comment = "0.3" [features] default = [] +# NOP features, solely so that people do not have to change their Cargo.toml +unstable_offset_of = [] unstable_const = [] diff --git a/README.OpenSource b/README.OpenSource index f5a71f8..25c752b 100644 --- a/README.OpenSource +++ b/README.OpenSource @@ -3,7 +3,7 @@ "Name": "memoffset", "License": "Apache License V2.0", "License File": "LICENSE", - "Version Number": "0.7.1", + "Version Number": "0.9.1", "Owner": "fangting12@huawei.com", "Upstream URL": "https://github.com/Gilnaa/memoffset", "Description": "A Rust library that provides support for calculating offsets in memory." diff --git a/README.md b/README.md index e297b33..fd621db 100644 --- a/README.md +++ b/README.md @@ -7,16 +7,19 @@ C-Like `offset_of` functionality for Rust structs. Introduces the following macros: * `offset_of!` for obtaining the offset of a member of a struct. * `offset_of_tuple!` for obtaining the offset of a member of a tuple. (Requires Rust 1.20+) + * `offset_of_union!` for obtaining the offset of a member of a union. * `span_of!` for obtaining the range that a field, or fields, span. `memoffset` works under `no_std` environments. +If you're using a rustc version greater or equal to 1.77, this crate's `offset_of!()` macro simply forwards to `core::mem::offset_of!()`. + ## Usage ## Add the following dependency to your `Cargo.toml`: ```toml [dependencies] -memoffset = "0.7" +memoffset = "0.9" ``` These versions will compile fine with rustc versions greater or equal to 1.19. @@ -45,21 +48,8 @@ fn main() { } ``` -## Feature flags ## +## Usage in constants ## +`memoffset` has support for compile-time `offset_of!` on rust>=1.65. -### Usage in constants ### -`memoffset` has **experimental** support for compile-time `offset_of!` on a nightly compiler. - -In order to use it, you must enable the `unstable_const` crate feature and several compiler features. - -Cargo.toml: -```toml -[dependencies.memoffset] -version = "0.7" -features = ["unstable_const"] -``` - -Your crate root: (`lib.rs`/`main.rs`) -```rust,ignore -#![feature(const_ptr_offset_from, const_refs_to_cell)] -``` +On versions below 1.77, this is an incomplete implementation with one caveat: +Due to dependence on [`#![feature(const_refs_to_cell)]`](https://github.com/rust-lang/rust/issues/80384), you cannot get the offset of a `Cell` field in a const-context. diff --git a/build.rs b/build.rs index 0604c19..0ef8901 100644 --- a/build.rs +++ b/build.rs @@ -19,4 +19,10 @@ fn main() { if ac.probe_rustc_version(1, 51) { println!("cargo:rustc-cfg=raw_ref_macros"); } + if ac.probe_rustc_version(1, 65) { + println!("cargo:rustc-cfg=stable_const"); + } + if ac.probe_rustc_version(1, 77) { + println!("cargo:rustc-cfg=stable_offset_of"); + } } diff --git a/src/lib.rs b/src/lib.rs index d80ff17..7e305b3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,11 +32,9 @@ //! a: u32 //! } //! -//! fn main() { -//! assert_eq!(offset_of!(HelpMeIAmTrappedInAStructFactory, a), 15); -//! assert_eq!(span_of!(HelpMeIAmTrappedInAStructFactory, a), 15..19); -//! assert_eq!(span_of!(HelpMeIAmTrappedInAStructFactory, help_me_before_they_ .. a), 0..15); -//! } +//! assert_eq!(offset_of!(HelpMeIAmTrappedInAStructFactory, a), 15); +//! assert_eq!(span_of!(HelpMeIAmTrappedInAStructFactory, a), 15..19); +//! assert_eq!(span_of!(HelpMeIAmTrappedInAStructFactory, help_me_before_they_ .. a), 0..15); //! ``` //! //! This functionality can be useful, for example, for checksum calculations: @@ -56,10 +54,6 @@ //! ``` #![no_std] -#![cfg_attr( - feature = "unstable_const", - feature(const_ptr_offset_from, const_refs_to_cell) -)] #[macro_use] #[cfg(doctests)] diff --git a/src/offset_of.rs b/src/offset_of.rs index d070181..33bf2bb 100644 --- a/src/offset_of.rs +++ b/src/offset_of.rs @@ -46,7 +46,7 @@ macro_rules! _memoffset__let_base_ptr { } /// Macro to compute the distance between two pointers. -#[cfg(feature = "unstable_const")] +#[cfg(stable_const)] #[macro_export] #[doc(hidden)] macro_rules! _memoffset_offset_from_unsafe { @@ -58,7 +58,7 @@ macro_rules! _memoffset_offset_from_unsafe { unsafe { (field as *const u8).offset_from(base as *const u8) as usize } }}; } -#[cfg(not(feature = "unstable_const"))] +#[cfg(not(stable_const))] #[macro_export] #[doc(hidden)] macro_rules! _memoffset_offset_from_unsafe { @@ -67,6 +67,27 @@ macro_rules! _memoffset_offset_from_unsafe { ($field as usize) - ($base as usize) }; } +#[cfg(not(stable_offset_of))] +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! _memoffset__offset_of_impl { + ($parent:path, $field:tt) => {{ + // Get a base pointer (non-dangling if rustc supports `MaybeUninit`). + _memoffset__let_base_ptr!(base_ptr, $parent); + // Get field pointer. + let field_ptr = raw_field!(base_ptr, $parent, $field); + // Compute offset. + _memoffset_offset_from_unsafe!(field_ptr, base_ptr) + }}; +} +#[cfg(stable_offset_of)] +#[macro_export] +#[doc(hidden)] +macro_rules! _memoffset__offset_of_impl { + ($parent:path, $field:tt) => {{ + $crate::__priv::mem::offset_of!($parent, $field) + }}; +} /// Calculates the offset of the specified field from the start of the named struct. /// @@ -81,10 +102,8 @@ macro_rules! _memoffset_offset_from_unsafe { /// c: [u8; 5] /// } /// -/// fn main() { -/// assert_eq!(offset_of!(Foo, a), 0); -/// assert_eq!(offset_of!(Foo, b), 4); -/// } +/// assert_eq!(offset_of!(Foo, a), 0); +/// assert_eq!(offset_of!(Foo, b), 4); /// ``` /// /// ## Notes @@ -98,39 +117,79 @@ macro_rules! _memoffset_offset_from_unsafe { /// As a result, the value should not be retained and used between different compilations. #[macro_export(local_inner_macros)] macro_rules! offset_of { - ($parent:path, $field:tt) => {{ + ($parent:path, $field:tt) => { + // Macro implementation is delegated to another macro to have a + // single top-level macro to attach documentation to. + _memoffset__offset_of_impl!($parent, $field) + }; +} + +#[cfg(tuple_ty)] +#[cfg(not(stable_offset_of))] +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! _memoffset__offset_of_tuple_impl { + ($parent:ty, $field:tt) => {{ // Get a base pointer (non-dangling if rustc supports `MaybeUninit`). _memoffset__let_base_ptr!(base_ptr, $parent); // Get field pointer. - let field_ptr = raw_field!(base_ptr, $parent, $field); + let field_ptr = raw_field_tuple!(base_ptr, $parent, $field); // Compute offset. _memoffset_offset_from_unsafe!(field_ptr, base_ptr) }}; } +#[cfg(tuple_ty)] +#[cfg(stable_offset_of)] +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! _memoffset__offset_of_tuple_impl { + ($parent:ty, $field:tt) => {{ + $crate::__priv::mem::offset_of!($parent, $field) + }}; +} + /// Calculates the offset of the specified field from the start of the tuple. /// /// ## Examples /// ``` /// use memoffset::offset_of_tuple; /// -/// fn main() { -/// assert!(offset_of_tuple!((u8, u32), 1) >= 0, "Tuples do not have a defined layout"); -/// } +/// assert!(offset_of_tuple!((u8, u32), 1) >= 0, "Tuples do not have a defined layout"); /// ``` #[cfg(tuple_ty)] #[macro_export(local_inner_macros)] macro_rules! offset_of_tuple { ($parent:ty, $field:tt) => {{ + // Macro implementation is delegated to another macro to have a + // single top-level macro to attach documentation to. + _memoffset__offset_of_tuple_impl!($parent, $field) + }}; +} + +#[cfg(not(stable_offset_of))] +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! _memoffset__offset_of_union_impl { + ($parent:path, $field:tt) => {{ // Get a base pointer (non-dangling if rustc supports `MaybeUninit`). _memoffset__let_base_ptr!(base_ptr, $parent); // Get field pointer. - let field_ptr = raw_field_tuple!(base_ptr, $parent, $field); + let field_ptr = raw_field_union!(base_ptr, $parent, $field); // Compute offset. _memoffset_offset_from_unsafe!(field_ptr, base_ptr) }}; } +#[cfg(stable_offset_of)] +#[macro_export(local_inner_macros)] +#[doc(hidden)] +macro_rules! _memoffset__offset_of_union_impl { + ($parent:path, $field:tt) => {{ + $crate::__priv::mem::offset_of!($parent, $field) + }}; +} + /// Calculates the offset of the specified union member from the start of the union. /// /// ## Examples @@ -143,29 +202,26 @@ macro_rules! offset_of_tuple { /// foo64: i64, /// } /// -/// fn main() { -/// assert!(offset_of_union!(Foo, foo64) == 0); -/// } +/// assert!(offset_of_union!(Foo, foo64) == 0); /// ``` /// /// ## Note -/// Due to macro_rules limitations, this macro will accept structs with a single field as well as unions. +/// Due to `macro_rules!` limitations, this macro will accept structs with a single field as well as unions. /// This is not a stable guarantee, and future versions of this crate might fail /// on any use of this macro with a struct, without a semver bump. #[macro_export(local_inner_macros)] macro_rules! offset_of_union { ($parent:path, $field:tt) => {{ - // Get a base pointer (non-dangling if rustc supports `MaybeUninit`). - _memoffset__let_base_ptr!(base_ptr, $parent); - // Get field pointer. - let field_ptr = raw_field_union!(base_ptr, $parent, $field); - // Compute offset. - _memoffset_offset_from_unsafe!(field_ptr, base_ptr) + // Macro implementation is delegated to another macro to have a + // single top-level macro to attach documentation to. + _memoffset__offset_of_union_impl!($parent, $field) }}; } #[cfg(test)] mod tests { + #![cfg_attr(allow_clippy, allow(clippy::identity_op))] // For `... + 0` constructs below. + #[test] fn offset_simple() { #[repr(C)] @@ -181,7 +237,6 @@ mod tests { } #[test] - #[cfg_attr(miri, ignore)] // this creates unaligned references fn offset_simple_packed() { #[repr(C, packed)] struct Foo { @@ -312,7 +367,7 @@ mod tests { assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, c) as usize); } - #[cfg(feature = "unstable_const")] + #[cfg(any(stable_offset_of, stable_const))] #[test] fn const_offset() { #[repr(C)] @@ -325,7 +380,7 @@ mod tests { assert_eq!([0; offset_of!(Foo, b)].len(), 4); } - #[cfg(feature = "unstable_const")] + #[cfg(stable_offset_of)] #[test] fn const_offset_interior_mutable() { #[repr(C)] @@ -337,7 +392,7 @@ mod tests { assert_eq!([0; offset_of!(Foo, b)].len(), 4); } - #[cfg(feature = "unstable_const")] + #[cfg(any(stable_offset_of, stable_const))] #[test] fn const_fn_offset() { const fn test_fn() -> usize { diff --git a/src/raw_field.rs b/src/raw_field.rs index e16df9f..c9e81fd 100644 --- a/src/raw_field.rs +++ b/src/raw_field.rs @@ -40,7 +40,7 @@ macro_rules! _memoffset__addr_of { /// Deref-coercion protection macro. /// -/// Prevents complilation if the specified field name is not a part of the +/// Prevents compilation if the specified field name is not a part of the /// struct definition. /// /// ```compile_fail @@ -80,7 +80,7 @@ macro_rules! _memoffset__field_check { /// Deref-coercion protection macro. /// -/// Prevents complilation if the specified type is not a tuple. +/// Prevents compilation if the specified type is not a tuple. /// /// ```compile_fail /// use memoffset::_memoffset__field_check_tuple; @@ -176,7 +176,7 @@ macro_rules! raw_field { } /// Computes a const raw pointer to the given field of the given base pointer -/// to the given parent tuple typle. +/// to the given parent tuple type. /// /// The `base` pointer *must not* be dangling, but it *may* point to /// uninitialized memory. @@ -198,7 +198,7 @@ macro_rules! raw_field_tuple { } /// Computes a const raw pointer to the given field of the given base pointer -/// to the given parent tuple typle. +/// to the given parent tuple type. /// /// The `base` pointer *must not* be dangling, but it *may* point to /// uninitialized memory. @@ -206,7 +206,7 @@ macro_rules! raw_field_tuple { /// ## Note /// This macro is the same as `raw_field`, except for a different Deref-coercion check that /// supports unions. -/// Due to macro_rules limitations, this check will accept structs with a single field as well as unions. +/// Due to `macro_rules!` limitations, this check will accept structs with a single field as well as unions. /// This is not a stable guarantee, and future versions of this crate might fail /// on any use of this macro with a struct, without a semver bump. #[macro_export(local_inner_macros)] diff --git a/src/span_of.rs b/src/span_of.rs index 89fccce..5543b92 100644 --- a/src/span_of.rs +++ b/src/span_of.rs @@ -60,7 +60,7 @@ macro_rules! _memoffset__compile_error { /// ### Safety /// The inter-field form mentioned above assumes that the first field is positioned before the /// second. -/// This is only guarenteed for `repr(C)` structs. +/// This is only guaranteed for `repr(C)` structs. /// Usage with `repr(Rust)` structs may yield unexpected results, like downward-going ranges, /// spans that include unexpected fields, empty spans, or spans that include *unexpected* padding bytes. /// @@ -81,15 +81,13 @@ macro_rules! _memoffset__compile_error { /// egg: [[u8; 4]; 4] /// } /// -/// fn main() { -/// assert_eq!(0..84, span_of!(Blarg, ..)); -/// assert_eq!(0..8, span_of!(Blarg, .. y)); -/// assert_eq!(0..64, span_of!(Blarg, ..= y)); -/// assert_eq!(0..8, span_of!(Blarg, x)); -/// assert_eq!(8..84, span_of!(Blarg, y ..)); -/// assert_eq!(0..8, span_of!(Blarg, x .. y)); -/// assert_eq!(0..64, span_of!(Blarg, x ..= y)); -/// } +/// assert_eq!(0..84, span_of!(Blarg, ..)); +/// assert_eq!(0..8, span_of!(Blarg, .. y)); +/// assert_eq!(0..64, span_of!(Blarg, ..= y)); +/// assert_eq!(0..8, span_of!(Blarg, x)); +/// assert_eq!(8..84, span_of!(Blarg, y ..)); +/// assert_eq!(0..8, span_of!(Blarg, x .. y)); +/// assert_eq!(0..64, span_of!(Blarg, x ..= y)); /// ``` #[macro_export(local_inner_macros)] macro_rules! span_of {