memoffset-0.8.0/.cargo_vcs_info.json0000644000000001360000000000100130260ustar { "git": { "sha1": "0fac3ac6642dd017a36268c4cdba2f04ec050d11" }, "path_in_vcs": "" }memoffset-0.8.0/.github/workflows/ci.yml000064400000000000000000000052121046102023000163310ustar 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! - 1.65.0 # Oldest supported with stable const evaluation (sans cell) - 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: Install Miri run: | rustup toolchain install nightly --component miri rustup override set nightly cargo miri setup - 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 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.8.0/.gitignore000064400000000000000000000000711046102023000136040ustar 00000000000000/target/ **/*.rs.bk Cargo.lock .idea/ cmake-build-debug/ memoffset-0.8.0/Cargo.toml0000644000000016620000000000100110310ustar # 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.8.0" 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.8.0/Cargo.toml.orig000064400000000000000000000006741046102023000145140ustar 00000000000000[package] name = "memoffset" version = "0.8.0" 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.8.0/LICENSE000064400000000000000000000020371046102023000126250ustar 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.8.0/README.md000064400000000000000000000051031046102023000130740ustar 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+) * `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. ## Usage ## Add the following dependency to your `Cargo.toml`: ```toml [dependencies] memoffset = "0.8" ``` 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); } ``` ## Usage in constants ## `memoffset` has support for compile-time `offset_of!` on rust>=1.65, or on older nightly compilers. ### Usage on stable Rust ### Constant evaluation is automatically enabled and avilable on stable compilers starting with rustc 1.65. 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. This means that if need to get the offset of a cell, you'll have to remain on nightly for now. ### Usage on recent nightlies ### If you're using a new-enough nightly and you require the ability to get the offset of a `Cell`, you'll have to enable the `unstable_const` cargo feature, as well as enabling `const_refs_to_cell` in your crate root. Do note that `unstable_const` is an unstable feature that is set to be removed in a future version of `memoffset`. Cargo.toml: ```toml [dependencies.memoffset] version = "0.8" features = ["unstable_const"] ``` Your crate root: (`lib.rs`/`main.rs`) ```rust,ignore #![feature(const_refs_to_cell)] ``` ### Usage on older nightlies ### In order to use it on an older nightly compiler, you must enable the `unstable_const` crate feature and several compiler features. Your crate root: (`lib.rs`/`main.rs`) ```rust,ignore #![feature(const_ptr_offset_from, const_refs_to_cell)] ``` memoffset-0.8.0/build.rs000064400000000000000000000012551046102023000132660ustar 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"); } if ac.probe_rustc_version(1, 65) { println!("cargo:rustc-cfg=stable_const"); } } memoffset-0.8.0/src/lib.rs000064400000000000000000000056541046102023000135330ustar 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( all(feature = "unstable_const", not(stable_const)), feature(const_ptr_offset_from) )] #![cfg_attr(feature = "unstable_const", feature(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.8.0/src/offset_of.rs000064400000000000000000000246001046102023000147270ustar 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(any(feature = "unstable_const", stable_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(any(feature = "unstable_const", stable_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); /// } /// ``` /// /// ## Notes /// Rust's ABI is unstable, and [type layout can be changed with each /// compilation](https://doc.rust-lang.org/reference/type-layout.html). /// /// Using `offset_of!` with a `repr(Rust)` struct will return the correct offset of the /// specified `field` for a particular compilation, but the exact value may change /// based on the compiler version, concrete struct type, time of day, or rustc's mood. /// /// 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) => {{ // 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) }}; } /// Calculates the offset of the specified union member from the start of the union. /// /// ## Examples /// ``` /// use memoffset::offset_of_union; /// /// #[repr(C, packed)] /// union Foo { /// foo32: i32, /// foo64: i64, /// } /// /// fn main() { /// 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. /// 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) }}; } #[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 offset_union() { // Since we're specifying repr(C), all fields are supposed to be at offset 0 #[repr(C)] union Foo { a: u32, b: [u8; 2], c: i64, } assert_eq!(offset_of_union!(Foo, a), 0); assert_eq!(offset_of_union!(Foo, b), 0); assert_eq!(offset_of_union!(Foo, c), 0); } #[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 ); } #[test] fn test_raw_field_union() { #[repr(C)] union Foo { a: u32, b: [u8; 2], c: i64, } let f = Foo { a: 0 }; let f_ptr = &f as *const _; assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, a) as usize); assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, b) as usize); assert_eq!(f_ptr as usize + 0, raw_field_union!(f_ptr, Foo, c) as usize); } #[cfg(any(feature = "unstable_const", stable_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(any(feature = "unstable_const", stable_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.8.0/src/raw_field.rs000064400000000000000000000170241046102023000147130ustar 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. /// /// Prevents complilation if the specified field name is not a part of the /// struct definition. /// /// ```compile_fail /// use memoffset::_memoffset__field_check; /// /// struct Foo { /// foo: i32, /// } /// /// type BoxedFoo = Box; /// /// _memoffset__field_check!(BoxedFoo, foo); /// ``` #[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. /// /// Prevents complilation if the specified type is not a tuple. /// /// ```compile_fail /// use memoffset::_memoffset__field_check_tuple; /// /// _memoffset__field_check_tuple!(i32, 0); /// ``` #[cfg(allow_clippy)] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__field_check_tuple { ($type:ty, $field:tt) => { // Make sure the type argument is a tuple #[allow(clippy::unneeded_wildcard_pattern)] let (_, ..): $type; }; } #[cfg(not(allow_clippy))] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__field_check_tuple { ($type:ty, $field:tt) => { // Make sure the type argument is a tuple let (_, ..): $type; }; } /// Deref-coercion protection macro for unions. /// Unfortunately accepts single-field structs as well, which is not ideal, /// but ultimately pretty harmless. /// /// ```compile_fail /// use memoffset::_memoffset__field_check_union; /// /// union Foo { /// variant_a: i32, /// } /// /// type BoxedFoo = Box; /// /// _memoffset__field_check_union!(BoxedFoo, variant_a); /// ``` #[cfg(allow_clippy)] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__field_check_union { ($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_wildcard_pattern)] // rustc1.19 requires unsafe here for the pattern; not needed in newer versions #[allow(unused_unsafe)] unsafe { let $type { $field: _ }; } }; } #[cfg(not(allow_clippy))] #[macro_export] #[doc(hidden)] macro_rules! _memoffset__field_check_union { ($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. // rustc1.19 requires unsafe here for the pattern; not needed in newer versions #[allow(unused_unsafe)] unsafe { let $type { $field: _ }; } }; } /// 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) } }}; } /// 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. /// /// ## 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. /// 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! raw_field_union { ($base:expr, $parent:path, $field:tt) => {{ _memoffset__field_check_union!($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.8.0/src/span_of.rs000064400000000000000000000207221046102023000144030ustar 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. /// /// ### 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. /// 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. /// /// ## 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]>() ); } }