/*! This crate expose the `FieldOffsets` derive macro and the types it uses. The macro allows to get const FieldOffset for member of a `#[repr(C)]` struct. The `FieldOffset` type is re-exported from the `field-offset` crate. */ #![no_std] #[cfg(test)] extern crate alloc; use core::pin::Pin; #[doc(inline)] pub use const_field_offset_macro::FieldOffsets; pub use field_offset::{AllowPin, FieldOffset, NotPinned}; /// This trait needs to be implemented if you use the `#[pin_drop]` attribute. It enables /// you to implement Drop for your type safely. pub trait PinnedDrop { /// This is the equivalent to the regular Drop trait with the difference that self /// is pinned. fn drop(self: Pin<&mut Self>); #[doc(hidden)] fn do_safe_pinned_drop(&mut self) { let p = unsafe { Pin::new_unchecked(self) }; p.drop() } } #[cfg(test)] mod tests { use super::*; use crate as const_field_offset; // ### Structures were change to repr(c) and to inherit FieldOffsets // Example structs #[derive(Debug, FieldOffsets)] #[repr(C)] struct Foo { a: u32, b: f64, c: bool, } #[derive(Debug, FieldOffsets)] #[repr(C)] struct Bar { x: u32, y: Foo, } #[test] fn test_simple() { // Get a pointer to `b` within `Foo` let foo_b = Foo::FIELD_OFFSETS.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!(*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!(x.b == 42.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 = Bar::FIELD_OFFSETS.y + Foo::FIELD_OFFSETS.b; // Apply the pointer to get at `b` and mutate it { let y = bar_y_b.apply_mut(&mut x); *y = 42.0; } assert!(x.y.b == 42.0); } #[test] fn test_pin() { use ::alloc::boxed::Box; // Get a pointer to `b` within `Foo` let foo_b = Foo::FIELD_OFFSETS.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!(*pb == 22.0); let mut x = Box::pin(Bar { x: 0, y: Foo { a: 1, b: 52.0, c: false } }); let bar_y_b = Bar::FIELD_OFFSETS.y + foo_b_pin; assert!(*bar_y_b.apply(&*x) == 52.0); let bar_y_pin = unsafe { Bar::FIELD_OFFSETS.y.as_pinned_projection() }; *(bar_y_pin + foo_b_pin).apply_pin_mut(x.as_mut()) = 12.; assert!(x.y.b == 12.0); } } #[doc(hidden)] #[cfg(feature = "field-offset-trait")] mod internal { use super::*; pub trait CombineFlag { type Output; } impl CombineFlag for (AllowPin, AllowPin) { type Output = AllowPin; } impl CombineFlag for (NotPinned, AllowPin) { type Output = NotPinned; } impl CombineFlag for (AllowPin, NotPinned) { type Output = NotPinned; } impl CombineFlag for (NotPinned, NotPinned) { type Output = NotPinned; } } #[cfg(feature = "field-offset-trait")] pub trait ConstFieldOffset: Copy { /// The type of the container type Container; /// The type of the field type Field; /// Can be AllowPin or NotPinned type PinFlag; const OFFSET: FieldOffset; fn as_field_offset(self) -> FieldOffset { Self::OFFSET } fn get_byte_offset(self) -> usize { Self::OFFSET.get_byte_offset() } fn apply(self, x: &Self::Container) -> &Self::Field { Self::OFFSET.apply(x) } fn apply_mut(self, x: &mut Self::Container) -> &mut Self::Field { Self::OFFSET.apply_mut(x) } fn apply_pin<'a>(self, x: Pin<&'a Self::Container>) -> Pin<&'a Self::Field> where Self: ConstFieldOffset, { Self::OFFSET.apply_pin(x) } fn apply_pin_mut<'a>(self, x: Pin<&'a mut Self::Container>) -> Pin<&'a mut Self::Field> where Self: ConstFieldOffset, { Self::OFFSET.apply_pin_mut(x) } } /// This can be used to transmute a FieldOffset from a NotPinned to any pin flag. /// This is only valid if we know that the offset is actually valid for this Flag. #[cfg(feature = "field-offset-trait")] union TransmutePinFlag { x: FieldOffset, y: FieldOffset, } /// Helper class used as the result of the addition of two stype that implement the `ConstFieldOffset` trait #[derive(Copy, Clone)] #[cfg(feature = "field-offset-trait")] pub struct ConstFieldOffsetSum(pub A, pub B); #[cfg(feature = "field-offset-trait")] impl ConstFieldOffset for ConstFieldOffsetSum where A: ConstFieldOffset, (A::PinFlag, B::PinFlag): internal::CombineFlag, { type Container = A::Container; type Field = B::Field; type PinFlag = <(A::PinFlag, B::PinFlag) as internal::CombineFlag>::Output; const OFFSET: FieldOffset = unsafe { TransmutePinFlag { y: FieldOffset::new_from_offset( A::OFFSET.get_byte_offset() + B::OFFSET.get_byte_offset(), ), } .x }; } #[cfg(feature = "field-offset-trait")] impl ::core::ops::Add for ConstFieldOffsetSum where Self: ConstFieldOffset, Other: ConstFieldOffset::Field>, { type Output = ConstFieldOffsetSum; fn add(self, other: Other) -> Self::Output { ConstFieldOffsetSum(self, other) } }