diff options
Diffstat (limited to 'vendor/field-offset')
-rw-r--r-- | vendor/field-offset/.cargo-checksum.json | 1 | ||||
-rw-r--r-- | vendor/field-offset/Cargo.toml | 25 | ||||
-rw-r--r-- | vendor/field-offset/LICENSE-APACHE | 176 | ||||
-rw-r--r-- | vendor/field-offset/LICENSE-MIT | 21 | ||||
-rw-r--r-- | vendor/field-offset/README.md | 34 | ||||
-rw-r--r-- | vendor/field-offset/build.rs | 17 | ||||
-rw-r--r-- | vendor/field-offset/src/lib.rs | 528 |
7 files changed, 802 insertions, 0 deletions
diff --git a/vendor/field-offset/.cargo-checksum.json b/vendor/field-offset/.cargo-checksum.json new file mode 100644 index 000000000..ea52156b9 --- /dev/null +++ b/vendor/field-offset/.cargo-checksum.json @@ -0,0 +1 @@ +{"files":{"Cargo.toml":"a37868c945b9b9372bc52e8a87a6eb7841dd9cce5c76b75c7a69cf8a992a3014","LICENSE-APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE-MIT":"9dd44491b4cf95dcfa2963943b3ae91bff9a78d13049a9e0b53e0d0b0a9c8f4e","README.md":"7affe2265eb804c7f6ce1d9a95b499f4c88d8b902865d7b1f0122ec6163f07b8","build.rs":"96d9e17f58fc09f2231ed0571c542ff79c71e8cf89ae2c208928899ffb471308","src/lib.rs":"0917ce4c9ab3e15434182df60daabb7ded15f7b863938cdc7f73be60d6e9ae44"},"package":"a3cf3a800ff6e860c863ca6d4b16fd999db8b752819c1606884047b73e468535"}
\ No newline at end of file diff --git a/vendor/field-offset/Cargo.toml b/vendor/field-offset/Cargo.toml new file mode 100644 index 000000000..fccc0288d --- /dev/null +++ b/vendor/field-offset/Cargo.toml @@ -0,0 +1,25 @@ +# 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 = "field-offset" +version = "0.3.5" +authors = ["Diggory Blake <diggsey@googlemail.com>"] +description = "Safe pointer-to-member implementation" +readme = "README.md" +license = "MIT OR Apache-2.0" +repository = "https://github.com/Diggsey/rust-field-offset" + +[dependencies.memoffset] +version = "0.8" + +[build-dependencies.rustc_version] +version = "0.4.0" diff --git a/vendor/field-offset/LICENSE-APACHE b/vendor/field-offset/LICENSE-APACHE new file mode 100644 index 000000000..d9a10c0d8 --- /dev/null +++ b/vendor/field-offset/LICENSE-APACHE @@ -0,0 +1,176 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/vendor/field-offset/LICENSE-MIT b/vendor/field-offset/LICENSE-MIT new file mode 100644 index 000000000..b4ab2017c --- /dev/null +++ b/vendor/field-offset/LICENSE-MIT @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2016-2021 Diggory Blake, and other contributors. + +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. diff --git a/vendor/field-offset/README.md b/vendor/field-offset/README.md new file mode 100644 index 000000000..8ebdf7903 --- /dev/null +++ b/vendor/field-offset/README.md @@ -0,0 +1,34 @@ +# field-offset: safe pointer-to-member functionality
+
+This crate implements an `offset_of!(...)` macro which safely encapsulates
+a pointer-to-member.
+
+Example:
+```rust
+struct Foo {
+ x: u32,
+ y: f64
+}
+
+let foo_y = offset_of!(Foo => y);
+
+let mut a = Foo { x: 1, y: 2.0 };
+
+*foo_y.apply_mut(&mut a) = 3.0;
+
+assert!(a.y == 3.0);
+```
+
+The macro returns an instance of `FieldOffset<T, U>`, which represents a
+pointer to a field of type `U` within a containing type, `T`.
+
+The `FieldOffset` type implements `Add`. Applying the resulting field offset
+is equivalent to applying the first field offset, then applying the second
+field offset.
+
+The macro also supports accessing nested fields:
+
+```rust
+let bar_foo_y = offset_of!(Bar => foo: Foo => y);
+```
+
diff --git a/vendor/field-offset/build.rs b/vendor/field-offset/build.rs new file mode 100644 index 000000000..a2f7a0353 --- /dev/null +++ b/vendor/field-offset/build.rs @@ -0,0 +1,17 @@ +extern crate rustc_version; +use rustc_version::{version, version_meta, Channel, Version}; + +fn main() { + // Assert we haven't travelled back in time + assert!(version().unwrap().major >= 1); + + // Check for a minimum version + if version().unwrap() >= Version::parse("1.36.0").unwrap() { + println!("cargo:rustc-cfg=fieldoffset_maybe_uninit"); + println!("cargo:rustc-cfg=fieldoffset_has_alloc"); + } + + if version_meta().unwrap().channel == Channel::Nightly { + println!("cargo:rustc-cfg=fieldoffset_assert_in_const_fn"); + } +} diff --git a/vendor/field-offset/src/lib.rs b/vendor/field-offset/src/lib.rs new file mode 100644 index 000000000..b0a12c779 --- /dev/null +++ b/vendor/field-offset/src/lib.rs @@ -0,0 +1,528 @@ +#![no_std] +#![cfg_attr(fieldoffset_assert_in_const_fn, feature(const_panic))] +// Explicit lifetimes are clearer when we are working with raw pointers, +// as the compiler will not warn us if we specify lifetime constraints +// which are too lax. +#![allow(clippy::needless_lifetimes)] + +#[cfg(all(test, fieldoffset_has_alloc))] +extern crate alloc; + +use core::fmt; +use core::marker::PhantomData; +use core::mem; +use core::ops::Add; +use core::pin::Pin; + +#[doc(hidden)] +pub extern crate memoffset as __memoffset; // `pub` for macro availability + +/// Represents a pointer to a field of type `U` within the type `T` +/// +/// The `PinFlag` parameter can be set to `AllowPin` to enable the projection +/// from Pin<&T> to Pin<&U> +#[repr(transparent)] +pub struct FieldOffset<T, U, PinFlag = NotPinned>( + /// Offset in bytes of the field within the struct + usize, + /// A pointer-to-member can be thought of as a function from + /// `&T` to `&U` with matching lifetimes + /// + /// ```compile_fail + /// use field_offset::FieldOffset; + /// struct Foo<'a>(&'a str); + /// fn test<'a>(foo: &Foo<'a>, of: FieldOffset<Foo<'static>, &'static str>) -> &'static str { + /// let of2 : FieldOffset<Foo<'a>, &'static str> = of; // This must not compile + /// of2.apply(foo) + /// } + /// ``` + /// That should compile: + /// ``` + /// use field_offset::FieldOffset; + /// struct Foo<'a>(&'a str, &'static str); + /// fn test<'a>(foo: &'a Foo<'static>, of: FieldOffset<Foo, &'static str>) -> &'a str { + /// let of2 : FieldOffset<Foo<'static>, &'static str> = of; + /// of.apply(foo) + /// } + /// fn test2(foo: &Foo<'static>, of: FieldOffset<Foo, &'static str>) -> &'static str { + /// let of2 : FieldOffset<Foo<'static>, &'static str> = of; + /// of.apply(foo) + /// } + /// fn test3<'a>(foo: &'a Foo, of: FieldOffset<Foo<'a>, &'a str>) -> &'a str { + /// of.apply(foo) + /// } + /// ``` + PhantomData<(PhantomContra<T>, U, PinFlag)>, +); + +/// `fn` cannot appear directly in a type that need to be const. +/// Workaround that with an indirection +struct PhantomContra<T>(fn(T)); + +/// Type that can be used in the `PinFlag` parameter of `FieldOffset` to specify that +/// this projection is valid on Pin types. +/// See documentation of `FieldOffset::new_from_offset_pinned` +pub enum AllowPin {} + +/// Type that can be used in the `PinFlag` parameter of `FieldOffset` to specify that +/// this projection is not valid on Pin types. +pub enum NotPinned {} + +impl<T, U> FieldOffset<T, U, NotPinned> { + // Use MaybeUninit to get a fake T + #[cfg(fieldoffset_maybe_uninit)] + #[inline] + fn with_uninit_ptr<R, F: FnOnce(*const T) -> R>(f: F) -> R { + let uninit = mem::MaybeUninit::<T>::uninit(); + f(uninit.as_ptr()) + } + + // Use a dangling pointer to get a fake T + #[cfg(not(fieldoffset_maybe_uninit))] + #[inline] + fn with_uninit_ptr<R, F: FnOnce(*const T) -> R>(f: F) -> R { + f(mem::align_of::<T>() as *const T) + } + + /// Construct a field offset via a lambda which returns a reference + /// to the field in question. + /// + /// # Safety + /// + /// The lambda *must not* dereference the provided pointer or access the + /// inner value in any way as it may point to uninitialized memory. + /// + /// For the returned `FieldOffset` to be safe to use, the returned pointer + /// must be valid for *any* instance of `T`. For example, returning a pointer + /// to a field from an enum with multiple variants will produce a `FieldOffset` + /// which is unsafe to use. + pub unsafe fn new<F: for<'a> FnOnce(*const T) -> *const U>(f: F) -> Self { + let offset = Self::with_uninit_ptr(|base_ptr| { + let field_ptr = f(base_ptr); + (field_ptr as usize).wrapping_sub(base_ptr as usize) + }); + + // Construct an instance using the offset + Self::new_from_offset(offset) + } + /// Construct a field offset directly from a byte offset. + /// + /// # Safety + /// + /// For the returned `FieldOffset` to be safe to use, the field offset + /// must be valid for *any* instance of `T`. For example, returning the offset + /// to a field from an enum with multiple variants will produce a `FieldOffset` + /// which is unsafe to use. + #[inline] + pub const unsafe fn new_from_offset(offset: usize) -> Self { + // Sanity check: ensure that the field offset plus the field size + // is no greater than the size of the containing struct. This is + // not sufficient to make the function *safe*, but it does catch + // obvious errors like returning a reference to a boxed value, + // which is owned by `T` and so has the correct lifetime, but is not + // actually a field. + #[cfg(fieldoffset_assert_in_const_fn)] + assert!(offset + mem::size_of::<U>() <= mem::size_of::<T>()); + // On stable rust, we can still get an assert in debug mode, + // relying on the checked overflow behaviour + let _ = mem::size_of::<T>() - (offset + mem::size_of::<U>()); + + FieldOffset(offset, PhantomData) + } +} + +// Methods for applying the pointer to member +impl<T, U, PinFlag> FieldOffset<T, U, PinFlag> { + /// Apply the field offset to a native pointer. + #[inline] + pub fn apply_ptr(self, x: *const T) -> *const U { + ((x as usize) + self.0) as *const U + } + /// Apply the field offset to a native mutable pointer. + #[inline] + pub fn apply_ptr_mut(self, x: *mut T) -> *mut U { + ((x as usize) + self.0) as *mut U + } + /// Apply the field offset to a reference. + #[inline] + pub fn apply<'a>(self, x: &'a T) -> &'a U { + unsafe { &*self.apply_ptr(x) } + } + /// Apply the field offset to a mutable reference. + #[inline] + pub fn apply_mut<'a>(self, x: &'a mut T) -> &'a mut U { + unsafe { &mut *self.apply_ptr_mut(x) } + } + /// Get the raw byte offset for this field offset. + #[inline] + pub const fn get_byte_offset(self) -> usize { + self.0 + } + + // Methods for unapplying the pointer to member + + /// Unapply the field offset to a native pointer. + /// + /// # Safety + /// + /// *Warning: very unsafe!* + /// + /// This applies a negative offset to a pointer. If the safety + /// implications of this are not already clear to you, then *do + /// not* use this method. Also be aware that Rust has stronger + /// aliasing rules than other languages, so it may be UB to + /// dereference the resulting pointer even if it points to a valid + /// location, due to the presence of other live references. + #[inline] + pub unsafe fn unapply_ptr(self, x: *const U) -> *const T { + ((x as usize) - self.0) as *const T + } + /// Unapply the field offset to a native mutable pointer. + /// + /// # Safety + /// + /// *Warning: very unsafe!* + /// + /// This applies a negative offset to a pointer. If the safety + /// implications of this are not already clear to you, then *do + /// not* use this method. Also be aware that Rust has stronger + /// aliasing rules than other languages, so it may be UB to + /// dereference the resulting pointer even if it points to a valid + /// location, due to the presence of other live references. + #[inline] + pub unsafe fn unapply_ptr_mut(self, x: *mut U) -> *mut T { + ((x as usize) - self.0) as *mut T + } + /// Unapply the field offset to a reference. + /// + /// # Safety + /// + /// *Warning: very unsafe!* + /// + /// This applies a negative offset to a reference. If the safety + /// implications of this are not already clear to you, then *do + /// not* use this method. Also be aware that Rust has stronger + /// aliasing rules than other languages, so this method may cause UB + /// even if the resulting reference points to a valid location, due + /// to the presence of other live references. + #[inline] + pub unsafe fn unapply<'a>(self, x: &'a U) -> &'a T { + &*self.unapply_ptr(x) + } + /// Unapply the field offset to a mutable reference. + /// + /// # Safety + /// + /// *Warning: very unsafe!* + /// + /// This applies a negative offset to a reference. If the safety + /// implications of this are not already clear to you, then *do + /// not* use this method. Also be aware that Rust has stronger + /// aliasing rules than other languages, so this method may cause UB + /// even if the resulting reference points to a valid location, due + /// to the presence of other live references. + #[inline] + pub unsafe fn unapply_mut<'a>(self, x: &'a mut U) -> &'a mut T { + &mut *self.unapply_ptr_mut(x) + } + + /// Convert this offset to an offset that is allowed to go from `Pin<&T>` + /// to `Pin<&U>` + /// + /// # Safety + /// + /// The Pin safety rules for projection must be respected. These rules are + /// explained in the + /// [Pin documentation](https://doc.rust-lang.org/stable/std/pin/index.html#pinning-is-structural-for-field) + pub const unsafe fn as_pinned_projection(self) -> FieldOffset<T, U, AllowPin> { + FieldOffset::new_from_offset_pinned(self.get_byte_offset()) + } + + /// Remove the AllowPin flag + pub const fn as_unpinned_projection(self) -> FieldOffset<T, U, NotPinned> { + unsafe { FieldOffset::new_from_offset(self.get_byte_offset()) } + } +} + +impl<T, U> FieldOffset<T, U, AllowPin> { + /// Construct a field offset directly from a byte offset, which can be projected from + /// a pinned. + /// + /// # Safety + /// + /// In addition to the safety rules of FieldOffset::new_from_offset, the projection + /// from `Pin<&T>` to `Pin<&U>` must also be allowed. The rules are explained in the + /// [Pin documentation](https://doc.rust-lang.org/stable/std/pin/index.html#pinning-is-structural-for-field) + #[inline] + pub const unsafe fn new_from_offset_pinned(offset: usize) -> Self { + FieldOffset(offset, PhantomData) + } + + /// Apply the field offset to a pinned reference and return a pinned + /// reference to the field + #[inline] + pub fn apply_pin<'a>(self, x: Pin<&'a T>) -> Pin<&'a U> { + unsafe { x.map_unchecked(|x| self.apply(x)) } + } + /// Apply the field offset to a pinned mutable reference and return a + /// pinned mutable reference to the field + #[inline] + pub fn apply_pin_mut<'a>(self, x: Pin<&'a mut T>) -> Pin<&'a mut U> { + unsafe { x.map_unchecked_mut(|x| self.apply_mut(x)) } + } +} + +impl<T, U> From<FieldOffset<T, U, AllowPin>> for FieldOffset<T, U, NotPinned> { + fn from(other: FieldOffset<T, U, AllowPin>) -> Self { + other.as_unpinned_projection() + } +} + +/// Allow chaining pointer-to-members. +/// +/// Applying the resulting field offset is equivalent to applying the first +/// field offset, then applying the second field offset. +/// +/// The requirements on the generic type parameters ensure this is a safe operation. +impl<T, U, V> Add<FieldOffset<U, V>> for FieldOffset<T, U> { + type Output = FieldOffset<T, V>; + #[inline] + fn add(self, other: FieldOffset<U, V>) -> FieldOffset<T, V> { + FieldOffset(self.0 + other.0, PhantomData) + } +} +impl<T, U, V> Add<FieldOffset<U, V, AllowPin>> for FieldOffset<T, U, AllowPin> { + type Output = FieldOffset<T, V, AllowPin>; + #[inline] + fn add(self, other: FieldOffset<U, V, AllowPin>) -> FieldOffset<T, V, AllowPin> { + FieldOffset(self.0 + other.0, PhantomData) + } +} +impl<T, U, V> Add<FieldOffset<U, V>> for FieldOffset<T, U, AllowPin> { + type Output = FieldOffset<T, V>; + #[inline] + fn add(self, other: FieldOffset<U, V>) -> FieldOffset<T, V> { + FieldOffset(self.0 + other.0, PhantomData) + } +} +impl<T, U, V> Add<FieldOffset<U, V, AllowPin>> for FieldOffset<T, U> { + type Output = FieldOffset<T, V>; + #[inline] + fn add(self, other: FieldOffset<U, V, AllowPin>) -> FieldOffset<T, V> { + FieldOffset(self.0 + other.0, PhantomData) + } +} + +/// The debug implementation prints the byte offset of the field in hexadecimal. +impl<T, U, Flag> fmt::Debug for FieldOffset<T, U, Flag> { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + write!(f, "FieldOffset({:#x})", self.0) + } +} + +impl<T, U, Flag> Copy for FieldOffset<T, U, Flag> {} +impl<T, U, Flag> Clone for FieldOffset<T, U, Flag> { + fn clone(&self) -> Self { + *self + } +} + +/// This macro allows safe construction of a FieldOffset, +/// by generating a known to be valid lambda to pass to the +/// constructor. It takes a type, and the identifier of a field +/// within that type as input. +/// +/// Examples: +/// +/// Offset of field `Foo.bar` +/// +/// ```rust +/// # #[macro_use] +/// # extern crate field_offset; +/// # fn main() { +/// #[repr(C)] +/// struct Foo { foo: i32, bar: i32 } +/// assert_eq!(offset_of!(Foo => bar).get_byte_offset(), 4); +/// # } +/// ``` +/// +/// Offset of nested field `Foo.bar.x` +/// +/// ```rust +/// # #[macro_use] +/// # extern crate field_offset; +/// # fn main() { +/// struct Bar { a: u8, x: u8 } +/// struct Foo { foo: i32, bar: Bar } +/// assert_eq!(offset_of!(Foo => bar: Bar => x).get_byte_offset(), 5); +/// # } +/// ``` +#[macro_export] +macro_rules! offset_of { + ($t: path => $f: tt) => {{ + // Construct the offset + #[allow(unused_unsafe)] + unsafe { + $crate::FieldOffset::<$t, _>::new(|x| { + $crate::__memoffset::raw_field!(x, $t, $f) + }) + } + }}; + ($t: path => $f: ident: $($rest: tt)*) => { + offset_of!($t => $f) + offset_of!($($rest)*) + }; +} + +#[cfg(test)] +mod tests { + // Example structs + #[derive(Debug)] + struct Foo { + a: u32, + b: f64, + c: bool, + } + + #[derive(Debug)] + struct Bar { + x: u32, + y: Foo, + } + + #[derive(Debug)] + struct Tuple(i32, f64); + + #[test] + fn test_simple() { + // Get a pointer to `b` within `Foo` + let foo_b = offset_of!(Foo => b); + + // Construct an example `Foo` + let mut x = Foo { + a: 1, + b: 2.0, + c: false, + }; + + // Apply the pointer to get at `b` and read it + { + let y = foo_b.apply(&x); + assert_eq!(*y, 2.0); + } + + // Apply the pointer to get at `b` and mutate it + { + let y = foo_b.apply_mut(&mut x); + *y = 42.0; + } + assert_eq!(x.b, 42.0); + } + + #[test] + fn test_tuple() { + // Get a pointer to `b` within `Foo` + let tuple_1 = offset_of!(Tuple => 1); + + // Construct an example `Foo` + let mut x = Tuple(1, 42.0); + + // Apply the pointer to get at `b` and read it + { + let y = tuple_1.apply(&x); + assert_eq!(*y, 42.0); + } + + // Apply the pointer to get at `b` and mutate it + { + let y = tuple_1.apply_mut(&mut x); + *y = 5.0; + } + assert_eq!(x.1, 5.0); + } + + #[test] + fn test_nested() { + // Construct an example `Foo` + let mut x = Bar { + x: 0, + y: Foo { + a: 1, + b: 2.0, + c: false, + }, + }; + + // Combine the pointer-to-members + let bar_y_b = offset_of!(Bar => y: Foo => b); + + // Apply the pointer to get at `b` and mutate it + { + let y = bar_y_b.apply_mut(&mut x); + *y = 42.0; + } + assert_eq!(x.y.b, 42.0); + } + + struct Parameterized<T, U> { + x: T, + _y: U, + } + #[test] + fn test_type_parameter() { + let _ = offset_of!(Parameterized<Parameterized<bool, bool>, bool> => x: Parameterized<bool, bool> => x); + } + + #[test] + fn test_const() { + use crate::FieldOffset; + #[repr(C)] + struct SomeStruct { + a: u8, + b: u32, + } + const CONST_FIELD_OFFSET: FieldOffset<SomeStruct, u32> = + unsafe { FieldOffset::new_from_offset(4) }; + const CONST_VALUE: usize = CONST_FIELD_OFFSET.get_byte_offset(); + assert_eq!(offset_of!(SomeStruct => b).get_byte_offset(), CONST_VALUE); + + static STATIC_FIELD_OFFSET: FieldOffset<SomeStruct, u32> = + unsafe { FieldOffset::new_from_offset(4) }; + assert_eq!( + offset_of!(SomeStruct => b).get_byte_offset(), + STATIC_FIELD_OFFSET.get_byte_offset() + ); + } + + #[cfg(fieldoffset_has_alloc)] + #[test] + fn test_pin() { + use alloc::boxed::Box; + use core::pin::Pin; + + // Get a pointer to `b` within `Foo` + let foo_b = offset_of!(Foo => b); + let foo_b_pin = unsafe { foo_b.as_pinned_projection() }; + let foo = Box::pin(Foo { + a: 21, + b: 22.0, + c: true, + }); + let pb: Pin<&f64> = foo_b_pin.apply_pin(foo.as_ref()); + assert_eq!(*pb, 22.0); + + let mut x = Box::pin(Bar { + x: 0, + y: Foo { + a: 1, + b: 52.0, + c: false, + }, + }); + let bar_y_b = offset_of!(Bar => y: Foo => b); + assert!(*bar_y_b.apply(&*x) == 52.0); + + let bar_y_pin = unsafe { offset_of!(Bar => y).as_pinned_projection() }; + *(bar_y_pin + foo_b_pin).apply_pin_mut(x.as_mut()) = 12.; + assert_eq!(x.y.b, 12.0); + } +} |