Repository: Gilnaa/memoffset
Branch: master
Commit: 153fc3d50f03
Files: 11
Total size: 37.4 KB
Directory structure:
gitextract_8erlj0h4/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── README.md
├── build.rs
└── src/
├── lib.rs
├── offset_of.rs
├── raw_field.rs
└── span_of.rs
================================================
FILE CONTENTS
================================================
================================================
FILE: .github/workflows/ci.yml
================================================
name: CI
on: [push, pull_request]
jobs:
test:
name: Test Suite
runs-on: ubuntu-latest
strategy:
matrix:
rust:
- stable
- beta
- nightly
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- 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)
- 1.77.0 # Oldest supported with native `offset_of!`
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
- name: Run cargo test
# Exclude doctests here, as we don't want to clutter docs themselves
# with backwards compatibility workarounds.
run: cargo test --lib
miri:
name: Test Suite (Miri)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly
components: miri
- name: Test with Miri
run: |
cargo miri test
style:
name: lints and formatting
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
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
run: cargo fmt -- --check
================================================
FILE: .gitignore
================================================
/target/
**/*.rs.bk
Cargo.lock
.idea/
cmake-build-debug/
================================================
FILE: CHANGELOG.md
================================================
# 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)]`
================================================
FILE: Cargo.toml
================================================
[package]
name = "memoffset"
version = "0.9.1"
authors = ["Gilad Naaman <gilad.naaman@gmail.com>"]
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 = []
# NOP features, solely so that people do not have to change their Cargo.toml
unstable_offset_of = []
unstable_const = []
================================================
FILE: LICENSE
================================================
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.
================================================
FILE: README.md
================================================
# memoffset #
[](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.
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.9"
```
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.
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.
================================================
FILE: build.rs
================================================
extern 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");
}
if ac.probe_rustc_version(1, 77) {
println!("cargo:rustc-cfg=stable_offset_of");
}
}
================================================
FILE: src/lib.rs
================================================
// 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
//! }
//!
//! 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]
#[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<T>(_ptr: *const T) -> usize {
mem::size_of::<T>()
}
}
#[macro_use]
mod raw_field;
#[macro_use]
mod offset_of;
#[macro_use]
mod span_of;
================================================
FILE: src/offset_of.rs
================================================
// 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(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(stable_const))]
#[macro_export]
#[doc(hidden)]
macro_rules! _memoffset_offset_from_unsafe {
($field:expr, $base:expr) => {
// Compute offset.
($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.
///
/// ## Examples
/// ```
/// use memoffset::offset_of;
///
/// #[repr(C, packed)]
/// struct Foo {
/// a: u32,
/// b: u64,
/// c: [u8; 5]
/// }
///
/// 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) => {
// 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_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;
///
/// 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_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
/// ```
/// use memoffset::offset_of_union;
///
/// #[repr(C, packed)]
/// union Foo {
/// foo32: i32,
/// foo64: i64,
/// }
///
/// 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) => {{
// 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)]
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]
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>(T, U);
fn foo<T, U>(_: Pair<T, U>) -> usize {
offset_of!(Pair<T, U>, 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(stable_offset_of, 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(stable_offset_of)]
#[test]
fn const_offset_interior_mutable() {
#[repr(C)]
struct Foo {
a: u32,
b: core::cell::Cell<u32>,
}
assert_eq!([0; offset_of!(Foo, b)].len(), 4);
}
#[cfg(any(stable_offset_of, 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);
}
}
================================================
FILE: src/raw_field.rs
================================================
// 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 compilation 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<Foo>;
///
/// _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 compilation 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<Foo>;
///
/// _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 type.
///
/// 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 type.
///
/// 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)
}
}};
}
================================================
FILE: src/span_of.rs
================================================
// 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
/// <https://doc.rust-lang.org/edition-guide/rust-2018/macros/macro-changes.html#macros-using-local_inner_macros>.
#[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 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.
///
/// ## 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]
/// }
///
/// 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::<Blarg>(), 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::<Test>());
assert_eq!(
span_of!(Test, x..),
offset_of!(Test, x)..mem::size_of::<Test>()
);
assert_eq!(
span_of!(Test, y..),
offset_of!(Test, y)..mem::size_of::<Test>()
);
assert_eq!(
span_of!(Test, z..),
offset_of!(Test, z)..mem::size_of::<Test>()
);
assert_eq!(
span_of!(Test, egg..),
offset_of!(Test, egg)..mem::size_of::<Test>()
);
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]>()
);
}
}
gitextract_8erlj0h4/
├── .github/
│ └── workflows/
│ └── ci.yml
├── .gitignore
├── CHANGELOG.md
├── Cargo.toml
├── LICENSE
├── README.md
├── build.rs
└── src/
├── lib.rs
├── offset_of.rs
├── raw_field.rs
└── span_of.rs
SYMBOL INDEX (19 symbols across 4 files)
FILE: build.rs
function main (line 3) | fn main() {
FILE: src/lib.rs
function size_of_pointee (line 76) | pub fn size_of_pointee<T>(_ptr: *const T) -> usize {
FILE: src/offset_of.rs
function offset_simple (line 226) | fn offset_simple() {
function offset_simple_packed (line 240) | fn offset_simple_packed() {
function tuple_struct (line 254) | fn tuple_struct() {
function offset_union (line 263) | fn offset_union() {
function path (line 278) | fn path() {
function inside_generic_method (line 290) | fn inside_generic_method() {
function test_tuple_offset (line 302) | fn test_tuple_offset() {
function test_raw_field (line 314) | fn test_raw_field() {
function test_raw_field_tuple (line 335) | fn test_raw_field_tuple() {
function test_raw_field_union (line 355) | fn test_raw_field_union() {
function const_offset (line 372) | fn const_offset() {
function const_offset_interior_mutable (line 385) | fn const_offset_interior_mutable() {
function const_fn_offset (line 397) | fn const_fn_offset() {
FILE: src/span_of.rs
function span_simple (line 159) | fn span_simple() {
function span_simple_packed (line 174) | fn span_simple_packed() {
function span_forms (line 188) | fn span_forms() {
function ig_test (line 212) | fn ig_test() {
Condensed preview — 11 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (40K chars).
[
{
"path": ".github/workflows/ci.yml",
"chars": 2067,
"preview": "name: CI\n\non: [push, pull_request]\n\njobs:\n test:\n name: Test Suite\n runs-on: ubuntu-latest\n strategy:\n ma"
},
{
"path": ".gitignore",
"chars": 57,
"preview": "/target/\n**/*.rs.bk\nCargo.lock\n.idea/\ncmake-build-debug/\n"
},
{
"path": "CHANGELOG.md",
"chars": 742,
"preview": "# Changelog\n\n## Unreleased\n\n## v0.9.1 (26/03/2024)\n### Added\n - Added changelog\n\n### Changed\n - Clarify documentation ab"
},
{
"path": "Cargo.toml",
"chars": 545,
"preview": "[package]\nname = \"memoffset\"\nversion = \"0.9.1\"\nauthors = [\"Gilad Naaman <gilad.naaman@gmail.com>\"]\ndescription = \"offset"
},
{
"path": "LICENSE",
"chars": 1055,
"preview": "Copyright (c) 2017 Gilad Naaman\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this so"
},
{
"path": "README.md",
"chars": 1698,
"preview": "# memoffset #\n\n[](https://crates.io/crates/memoffset)\n\nC-Like `offset_"
},
{
"path": "build.rs",
"chars": 784,
"preview": "extern crate autocfg;\n\nfn main() {\n let ac = autocfg::new();\n\n // Check for a minimum version for a few features\n "
},
{
"path": "src/lib.rs",
"chars": 2777,
"preview": "// Copyright (c) 2017 Gilad Naaman\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n//"
},
{
"path": "src/offset_of.rs",
"chars": 12256,
"preview": "// Copyright (c) 2017 Gilad Naaman\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n//"
},
{
"path": "src/raw_field.rs",
"chars": 7699,
"preview": "// Copyright (c) 2020 Gilad Naaman, Ralf Jung\n//\n// Permission is hereby granted, free of charge, to any person obtainin"
},
{
"path": "src/span_of.rs",
"chars": 8608,
"preview": "// Copyright (c) 2017 Gilad Naaman\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n//"
}
]
About this extraction
This page contains the full source code of the Gilnaa/memoffset GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 11 files (37.4 KB), approximately 10.4k tokens, and a symbol index with 19 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.