memoffset-0.6.5/.cargo_vcs_info.json0000644000000001120000000000100130230ustar { "git": { "sha1": "01e2e42ef0d833682c27b0f40a2cc748d86b2dc3" } } memoffset-0.6.5/.github/workflows/ci.yml000064400000000000000000000045310072674642500163670ustar 00000000000000name: CI on: [push, pull_request] jobs: test: name: Test Suite runs-on: ubuntu-latest strategy: matrix: rust: - stable - beta - nightly steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: ${{ matrix.rust }} override: true - name: Run cargo test run: cargo test test-msrv: name: Test Suite runs-on: ubuntu-latest strategy: matrix: rust: - 1.19.0 # Oldest supported (first version with numeric fields in struct patterns) - 1.20.0 # Oldest supported with tuple_ty - 1.31.0 # Oldest supported with allow(clippy) - 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 steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 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: Test with Miri run: ci/miri.sh style: name: lints and formatting runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: toolchain: 1.51.0 # pin a version for reproducible results components: rustfmt override: true - name: Check warnings run: RUSTFLAGS="-D warnings" cargo check --all-targets - name: Check formatting run: cargo fmt -- --check memoffset-0.6.5/.gitignore000064400000000000000000000000710072674642500136370ustar 00000000000000/target/ **/*.rs.bk Cargo.lock .idea/ cmake-build-debug/ memoffset-0.6.5/Cargo.toml0000644000000016350000000000100110340ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] name = "memoffset" version = "0.6.5" authors = ["Gilad Naaman "] description = "offset_of functionality for Rust structs." readme = "README.md" keywords = ["mem", "offset", "offset_of", "offsetof"] categories = ["no-std"] license = "MIT" repository = "https://github.com/Gilnaa/memoffset" [dev-dependencies.doc-comment] version = "0.3" [build-dependencies.autocfg] version = "1" [features] default = [] unstable_const = [] memoffset-0.6.5/Cargo.toml.orig000064400000000000000000000006740072674642500145470ustar 00000000000000[package] name = "memoffset" version = "0.6.5" authors = ["Gilad Naaman "] description = "offset_of functionality for Rust structs." license = "MIT" readme = "README.md" repository = "https://github.com/Gilnaa/memoffset" keywords = ["mem", "offset", "offset_of", "offsetof"] categories = ["no-std"] [build-dependencies] autocfg = "1" [dev-dependencies] doc-comment = "0.3" [features] default = [] unstable_const = [] memoffset-0.6.5/LICENSE000064400000000000000000000020370072674642500126600ustar 00000000000000Copyright (c) 2017 Gilad Naaman Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.memoffset-0.6.5/README.md000064400000000000000000000030770072674642500131370ustar 00000000000000# memoffset # [![](https://img.shields.io/crates/v/memoffset.svg)](https://crates.io/crates/memoffset) 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+) * `span_of!` for obtaining the range that a field, or fields, span. `memoffset` works under `no_std` environments. ## Usage ## Add the following dependency to your `Cargo.toml`: ```toml [dependencies] memoffset = "0.6" ``` These versions will compile fine with rustc versions greater or equal to 1.19. ## Examples ## ```rust use memoffset::{offset_of, span_of}; #[repr(C, packed)] struct Foo { a: u32, b: u32, c: [u8; 5], d: u32, } fn main() { assert_eq!(offset_of!(Foo, b), 4); assert_eq!(offset_of!(Foo, d), 4+4+5); assert_eq!(span_of!(Foo, a), 0..4); assert_eq!(span_of!(Foo, a .. c), 0..8); assert_eq!(span_of!(Foo, a ..= c), 0..13); assert_eq!(span_of!(Foo, ..= d), 0..17); assert_eq!(span_of!(Foo, b ..), 4..17); } ``` ## Feature flags ## ### 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.6" features = ["unstable_const"] ``` Your crate root: (`lib.rs`/`main.rs`) ```rust,ignore #![feature(const_ptr_offset_from, const_refs_to_cell)] ``` memoffset-0.6.5/build.rs000064400000000000000000000011160072674642500133150ustar 00000000000000extern crate autocfg; fn main() { let ac = autocfg::new(); // Check for a minimum version for a few features if ac.probe_rustc_version(1, 20) { println!("cargo:rustc-cfg=tuple_ty"); } if ac.probe_rustc_version(1, 31) { println!("cargo:rustc-cfg=allow_clippy"); } if ac.probe_rustc_version(1, 36) { println!("cargo:rustc-cfg=maybe_uninit"); } if ac.probe_rustc_version(1, 40) { println!("cargo:rustc-cfg=doctests"); } if ac.probe_rustc_version(1, 51) { println!("cargo:rustc-cfg=raw_ref_macros"); } } memoffset-0.6.5/ci/miri.sh000075500000000000000000000006300072674642500135420ustar 00000000000000set -ex # Install Miri. MIRI_NIGHTLY=nightly-$(curl -s https://rust-lang.github.io/rustup-components-history/x86_64-unknown-linux-gnu/miri) echo "Installing latest nightly with Miri: $MIRI_NIGHTLY" rustup default "$MIRI_NIGHTLY" rustup component add miri # Run tests. cargo miri test cargo miri test --all-features # Restore old state in case Travis uses this cache for other jobs. rustup default nightly memoffset-0.6.5/src/lib.rs000064400000000000000000000055420072674642500135620ustar 00000000000000// Copyright (c) 2017 Gilad Naaman // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. //! A crate used for calculating offsets of struct members and their spans. //! //! This functionality currently can not be used in compile time code such as `const` or `const fn` definitions. //! //! ## Examples //! ``` //! use memoffset::{offset_of, span_of}; //! //! #[repr(C, packed)] //! struct HelpMeIAmTrappedInAStructFactory { //! help_me_before_they_: [u8; 15], //! 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); //! } //! ``` //! //! This functionality can be useful, for example, for checksum calculations: //! //! ```ignore //! #[repr(C, packed)] //! struct Message { //! header: MessageHeader, //! fragment_index: u32, //! fragment_count: u32, //! payload: [u8; 1024], //! checksum: u16 //! } //! //! let checksum_range = &raw[span_of!(Message, header..checksum)]; //! let checksum = crc16(checksum_range); //! ``` #![no_std] #![cfg_attr( feature = "unstable_const", feature(const_ptr_offset_from, const_refs_to_cell) )] #[macro_use] #[cfg(doctests)] #[cfg(doctest)] extern crate doc_comment; #[cfg(doctests)] #[cfg(doctest)] doctest!("../README.md"); /// Hidden module for things the macros need to access. #[doc(hidden)] pub mod __priv { #[doc(hidden)] pub use core::mem; #[doc(hidden)] pub use core::ptr; /// Use type inference to obtain the size of the pointee (without actually using the pointer). #[doc(hidden)] pub fn size_of_pointee(_ptr: *const T) -> usize { mem::size_of::() } } #[macro_use] mod raw_field; #[macro_use] mod offset_of; #[macro_use] mod span_of; memoffset-0.6.5/src/offset_of.rs000064400000000000000000000177140072674642500147720ustar 00000000000000// Copyright (c) 2017 Gilad Naaman // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. /// Macro to create a local `base_ptr` raw pointer of the given type, avoiding UB as /// much as is possible currently. #[cfg(maybe_uninit)] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__let_base_ptr { ($name:ident, $type:ty) => { // No UB here, and the pointer does not dangle, either. // But we have to make sure that `uninit` lives long enough, // so it has to be in the same scope as `$name`. That's why // `let_base_ptr` declares a variable (several, actually) // instead of returning one. let uninit = $crate::__priv::mem::MaybeUninit::<$type>::uninit(); let $name: *const $type = uninit.as_ptr(); }; } #[cfg(not(maybe_uninit))] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__let_base_ptr { ($name:ident, $type:ty) => { // No UB right here, but we will later dereference this pointer to // offset into a field, and that is UB because the pointer is dangling. let $name = $crate::__priv::mem::align_of::<$type>() as *const $type; }; } /// Macro to compute the distance between two pointers. #[cfg(feature = "unstable_const")] #[macro_export] #[doc(hidden)] macro_rules! _memoffset_offset_from_unsafe { ($field:expr, $base:expr) => {{ let field = $field; // evaluate $field outside the `unsafe` block let base = $base; // evaluate $base outside the `unsafe` block // Compute offset, with unstable `offset_from` for const-compatibility. // (Requires the pointers to not dangle, but we already need that for `raw_field!` anyway.) unsafe { (field as *const u8).offset_from(base as *const u8) as usize } }}; } #[cfg(not(feature = "unstable_const"))] #[macro_export] #[doc(hidden)] macro_rules! _memoffset_offset_from_unsafe { ($field:expr, $base:expr) => { // Compute offset. ($field as usize) - ($base as usize) }; } /// Calculates the offset of the specified field from the start of the named struct. /// /// ## Examples /// ``` /// use memoffset::offset_of; /// /// #[repr(C, packed)] /// struct Foo { /// a: u32, /// b: u64, /// c: [u8; 5] /// } /// /// fn main() { /// assert_eq!(offset_of!(Foo, a), 0); /// assert_eq!(offset_of!(Foo, b), 4); /// } /// ``` #[macro_export(local_inner_macros)] macro_rules! offset_of { ($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) }}; } /// 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"); /// } /// ``` #[cfg(tuple_ty)] #[macro_export(local_inner_macros)] macro_rules! offset_of_tuple { ($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_tuple!(base_ptr, $parent, $field); // Compute offset. _memoffset_offset_from_unsafe!(field_ptr, base_ptr) }}; } #[cfg(test)] mod tests { #[test] fn offset_simple() { #[repr(C)] struct Foo { a: u32, b: [u8; 2], c: i64, } assert_eq!(offset_of!(Foo, a), 0); assert_eq!(offset_of!(Foo, b), 4); assert_eq!(offset_of!(Foo, c), 8); } #[test] #[cfg_attr(miri, ignore)] // this creates unaligned references fn offset_simple_packed() { #[repr(C, packed)] struct Foo { a: u32, b: [u8; 2], c: i64, } assert_eq!(offset_of!(Foo, a), 0); assert_eq!(offset_of!(Foo, b), 4); assert_eq!(offset_of!(Foo, c), 6); } #[test] fn tuple_struct() { #[repr(C)] struct Tup(i32, i32); assert_eq!(offset_of!(Tup, 0), 0); assert_eq!(offset_of!(Tup, 1), 4); } #[test] fn path() { mod sub { #[repr(C)] pub struct Foo { pub x: u32, } } assert_eq!(offset_of!(sub::Foo, x), 0); } #[test] fn inside_generic_method() { struct Pair(T, U); fn foo(_: Pair) -> usize { offset_of!(Pair, 1) } assert_eq!(foo(Pair(0, 0)), 4); } #[cfg(tuple_ty)] #[test] fn test_tuple_offset() { let f = (0i32, 0.0f32, 0u8); let f_ptr = &f as *const _; let f1_ptr = &f.1 as *const _; assert_eq!( f1_ptr as usize - f_ptr as usize, offset_of_tuple!((i32, f32, u8), 1) ); } #[test] fn test_raw_field() { #[repr(C)] struct Foo { a: u32, b: [u8; 2], c: i64, } let f: Foo = Foo { a: 0, b: [0, 0], c: 0, }; let f_ptr = &f as *const _; assert_eq!(f_ptr as usize + 0, raw_field!(f_ptr, Foo, a) as usize); assert_eq!(f_ptr as usize + 4, raw_field!(f_ptr, Foo, b) as usize); assert_eq!(f_ptr as usize + 8, raw_field!(f_ptr, Foo, c) as usize); } #[cfg(tuple_ty)] #[test] fn test_raw_field_tuple() { let t = (0u32, 0u8, false); let t_ptr = &t as *const _; let t_addr = t_ptr as usize; assert_eq!( &t.0 as *const _ as usize - t_addr, raw_field_tuple!(t_ptr, (u32, u8, bool), 0) as usize - t_addr ); assert_eq!( &t.1 as *const _ as usize - t_addr, raw_field_tuple!(t_ptr, (u32, u8, bool), 1) as usize - t_addr ); assert_eq!( &t.2 as *const _ as usize - t_addr, raw_field_tuple!(t_ptr, (u32, u8, bool), 2) as usize - t_addr ); } #[cfg(feature = "unstable_const")] #[test] fn const_offset() { #[repr(C)] struct Foo { a: u32, b: [u8; 2], c: i64, } assert_eq!([0; offset_of!(Foo, b)].len(), 4); } #[cfg(feature = "unstable_const")] #[test] fn const_offset_interior_mutable() { #[repr(C)] struct Foo { a: u32, b: core::cell::Cell, } assert_eq!([0; offset_of!(Foo, b)].len(), 4); } #[cfg(feature = "unstable_const")] #[test] fn const_fn_offset() { const fn test_fn() -> usize { #[repr(C)] struct Foo { a: u32, b: [u8; 2], c: i64, } offset_of!(Foo, b) } assert_eq!([0; test_fn()].len(), 4); } } memoffset-0.6.5/src/raw_field.rs000064400000000000000000000102710072674642500147430ustar 00000000000000// Copyright (c) 2020 Gilad Naaman, Ralf Jung // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. /// `addr_of!`, or just ref-then-cast when that is not available. #[cfg(raw_ref_macros)] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__addr_of { ($path:expr) => {{ $crate::__priv::ptr::addr_of!($path) }}; } #[cfg(not(raw_ref_macros))] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__addr_of { ($path:expr) => {{ // This is UB because we create an intermediate reference to uninitialized memory. // Nothing we can do about that without `addr_of!` though. &$path as *const _ }}; } /// Deref-coercion protection macro. #[cfg(allow_clippy)] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__field_check { ($type:path, $field:tt) => { // Make sure the field actually exists. This line ensures that a // compile-time error is generated if $field is accessed through a // Deref impl. #[allow(clippy::unneeded_field_pattern)] let $type { $field: _, .. }; }; } #[cfg(not(allow_clippy))] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__field_check { ($type:path, $field:tt) => { // Make sure the field actually exists. This line ensures that a // compile-time error is generated if $field is accessed through a // Deref impl. let $type { $field: _, .. }; }; } /// Deref-coercion protection macro. #[macro_export] #[doc(hidden)] macro_rules! _memoffset__field_check_tuple { ($type:ty, $field:tt) => { // Make sure the type argument is a tuple let (_, ..): $type; }; } /// Computes a const raw pointer to the given field of the given base pointer /// to the given parent type. /// /// The `base` pointer *must not* be dangling, but it *may* point to /// uninitialized memory. #[macro_export(local_inner_macros)] macro_rules! raw_field { ($base:expr, $parent:path, $field:tt) => {{ _memoffset__field_check!($parent, $field); let base = $base; // evaluate $base outside the `unsafe` block // Get the field address. // Crucially, we know that this will not trigger a deref coercion because // of the field check we did above. #[allow(unused_unsafe)] // for when the macro is used in an unsafe block unsafe { _memoffset__addr_of!((*(base as *const $parent)).$field) } }}; } /// Computes a const raw pointer to the given field of the given base pointer /// to the given parent tuple typle. /// /// The `base` pointer *must not* be dangling, but it *may* point to /// uninitialized memory. #[cfg(tuple_ty)] #[macro_export(local_inner_macros)] macro_rules! raw_field_tuple { ($base:expr, $parent:ty, $field:tt) => {{ _memoffset__field_check_tuple!($parent, $field); let base = $base; // evaluate $base outside the `unsafe` block // Get the field address. // Crucially, we know that this will not trigger a deref coercion because // of the field check we did above. #[allow(unused_unsafe)] // for when the macro is used in an unsafe block unsafe { _memoffset__addr_of!((*(base as *const $parent)).$field) } }}; } memoffset-0.6.5/src/span_of.rs000064400000000000000000000201310072674642500144300ustar 00000000000000// Copyright (c) 2017 Gilad Naaman // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE // SOFTWARE. /// Reexport for `local_inner_macros`; see /// . #[doc(hidden)] #[macro_export] macro_rules! _memoffset__compile_error { ($($inner:tt)*) => { compile_error! { $($inner)* } } } /// Produces a range instance representing the sub-slice containing the specified member. /// /// This macro provides 2 forms of differing functionalities. /// /// The first form is identical to the appearance of the `offset_of!` macro. /// /// ```ignore /// span_of!(Struct, member) /// ``` /// /// The second form of `span_of!` returns a sub-slice which starts at one field, and ends at another. /// The general pattern of this form is: /// /// ```ignore /// // Exclusive /// span_of!(Struct, member_a .. member_b) /// // Inclusive /// span_of!(Struct, member_a ..= member_b) /// /// // Open-ended ranges /// span_of!(Struct, .. end) /// span_of!(Struct, start ..) /// ``` /// /// *Note*: /// This macro uses recursion in order to resolve the range expressions, so there is a limit to /// the complexity of the expression. /// In order to raise the limit, the compiler's recursion limit should be lifted. /// /// ## Examples /// ``` /// use memoffset::span_of; /// /// #[repr(C)] /// struct Florp { /// a: u32 /// } /// /// #[repr(C)] /// struct Blarg { /// x: [u32; 2], /// y: [u8; 56], /// z: Florp, /// 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)); /// } /// ``` #[macro_export(local_inner_macros)] macro_rules! span_of { (@helper $root:ident, [] ..=) => { _memoffset__compile_error!("Expected a range, found '..='") }; (@helper $root:ident, [] ..) => { _memoffset__compile_error!("Expected a range, found '..'") }; // No explicit begin for range. (@helper $root:ident, $parent:path, [] ..) => {{ ($root as usize, $root as usize + $crate::__priv::size_of_pointee($root)) }}; (@helper $root:ident, $parent:path, [] ..= $end:tt) => {{ let end = raw_field!($root, $parent, $end); ($root as usize, end as usize + $crate::__priv::size_of_pointee(end)) }}; (@helper $root:ident, $parent:path, [] .. $end:tt) => {{ ($root as usize, raw_field!($root, $parent, $end) as usize) }}; // Explicit begin and end for range. (@helper $root:ident, $parent:path, # $begin:tt [] ..= $end:tt) => {{ let begin = raw_field!($root, $parent, $begin); let end = raw_field!($root, $parent, $end); (begin as usize, end as usize + $crate::__priv::size_of_pointee(end)) }}; (@helper $root:ident, $parent:path, # $begin:tt [] .. $end:tt) => {{ (raw_field!($root, $parent, $begin) as usize, raw_field!($root, $parent, $end) as usize) }}; // No explicit end for range. (@helper $root:ident, $parent:path, # $begin:tt [] ..) => {{ (raw_field!($root, $parent, $begin) as usize, $root as usize + $crate::__priv::size_of_pointee($root)) }}; (@helper $root:ident, $parent:path, # $begin:tt [] ..=) => {{ _memoffset__compile_error!( "Found inclusive range to the end of a struct. Did you mean '..' instead of '..='?") }}; // Just one field. (@helper $root:ident, $parent:path, # $field:tt []) => {{ let field = raw_field!($root, $parent, $field); (field as usize, field as usize + $crate::__priv::size_of_pointee(field)) }}; // Parsing. (@helper $root:ident, $parent:path, $(# $begin:tt)+ [] $tt:tt $($rest:tt)*) => {{ span_of!(@helper $root, $parent, $(#$begin)* #$tt [] $($rest)*) }}; (@helper $root:ident, $parent:path, [] $tt:tt $($rest:tt)*) => {{ span_of!(@helper $root, $parent, #$tt [] $($rest)*) }}; // Entry point. ($sty:path, $($exp:tt)+) => ({ // Get a base pointer. _memoffset__let_base_ptr!(root, $sty); let base = root as usize; let (begin, end) = span_of!(@helper root, $sty, [] $($exp)*); begin-base..end-base }); } #[cfg(test)] mod tests { use core::mem; #[test] fn span_simple() { #[repr(C)] struct Foo { a: u32, b: [u8; 2], c: i64, } assert_eq!(span_of!(Foo, a), 0..4); assert_eq!(span_of!(Foo, b), 4..6); assert_eq!(span_of!(Foo, c), 8..8 + 8); } #[test] #[cfg_attr(miri, ignore)] // this creates unaligned references fn span_simple_packed() { #[repr(C, packed)] struct Foo { a: u32, b: [u8; 2], c: i64, } assert_eq!(span_of!(Foo, a), 0..4); assert_eq!(span_of!(Foo, b), 4..6); assert_eq!(span_of!(Foo, c), 6..6 + 8); } #[test] fn span_forms() { #[repr(C)] struct Florp { a: u32, } #[repr(C)] struct Blarg { x: u64, y: [u8; 56], z: Florp, egg: [[u8; 4]; 5], } // Love me some brute force assert_eq!(0..8, span_of!(Blarg, x)); assert_eq!(64..68, span_of!(Blarg, z)); assert_eq!(68..mem::size_of::(), span_of!(Blarg, egg)); assert_eq!(8..64, span_of!(Blarg, y..z)); assert_eq!(0..64, span_of!(Blarg, x..=y)); } #[test] fn ig_test() { #[repr(C)] struct Member { foo: u32, } #[repr(C)] struct Test { x: u64, y: [u8; 56], z: Member, egg: [[u8; 4]; 4], } assert_eq!(span_of!(Test, ..x), 0..0); assert_eq!(span_of!(Test, ..=x), 0..8); assert_eq!(span_of!(Test, ..y), 0..8); assert_eq!(span_of!(Test, ..=y), 0..64); assert_eq!(span_of!(Test, ..z), 0..64); assert_eq!(span_of!(Test, ..=z), 0..68); assert_eq!(span_of!(Test, ..egg), 0..68); assert_eq!(span_of!(Test, ..=egg), 0..84); assert_eq!(span_of!(Test, ..), 0..mem::size_of::()); assert_eq!( span_of!(Test, x..), offset_of!(Test, x)..mem::size_of::() ); assert_eq!( span_of!(Test, y..), offset_of!(Test, y)..mem::size_of::() ); assert_eq!( span_of!(Test, z..), offset_of!(Test, z)..mem::size_of::() ); assert_eq!( span_of!(Test, egg..), offset_of!(Test, egg)..mem::size_of::() ); assert_eq!( span_of!(Test, x..y), offset_of!(Test, x)..offset_of!(Test, y) ); assert_eq!( span_of!(Test, x..=y), offset_of!(Test, x)..offset_of!(Test, y) + mem::size_of::<[u8; 56]>() ); } }