slint/helper_crates/const-field-offset/src/lib.rs
2020-08-26 08:32:13 +02:00

217 lines
6.1 KiB
Rust

/*!
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<Self::Container, Self::Field, Self::PinFlag>;
fn as_field_offset(self) -> FieldOffset<Self::Container, Self::Field, Self::PinFlag> {
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<PinFlag = AllowPin>,
{
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<PinFlag = AllowPin>,
{
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<Container, Field, PinFlag> {
x: FieldOffset<Container, Field, PinFlag>,
y: FieldOffset<Container, Field>,
}
/// 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<A: ConstFieldOffset, B: ConstFieldOffset>(pub A, pub B);
#[cfg(feature = "field-offset-trait")]
impl<A: ConstFieldOffset, B: ConstFieldOffset> ConstFieldOffset for ConstFieldOffsetSum<A, B>
where
A: ConstFieldOffset<Field = B::Container>,
(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<Self::Container, Self::Field, Self::PinFlag> = unsafe {
TransmutePinFlag {
y: FieldOffset::new_from_offset(
A::OFFSET.get_byte_offset() + B::OFFSET.get_byte_offset(),
),
}
.x
};
}
#[cfg(feature = "field-offset-trait")]
impl<A: ConstFieldOffset, B: ConstFieldOffset, Other> ::core::ops::Add<Other>
for ConstFieldOffsetSum<A, B>
where
Self: ConstFieldOffset,
Other: ConstFieldOffset<Container = <Self as ConstFieldOffset>::Field>,
{
type Output = ConstFieldOffsetSum<Self, Other>;
fn add(self, other: Other) -> Self::Output {
ConstFieldOffsetSum(self, other)
}
}