offset/
lib.rs

1//! # Glorified offsets for arbitrary structures
2//!
3//! Why write `foo.bar`, when you could write
4//! `offset_of!(<Foo>::bar).index_in(foo)`?
5
6#![no_std]
7
8use core::cmp::Ordering;
9use core::fmt;
10use core::hash::{Hash, Hasher};
11use core::marker::PhantomData as marker;
12use core::mem::MaybeUninit;
13use core::ops::Add;
14
15/// An offset, representing a value of type `Field` into a value of type `Base`.
16///
17/// The value of the offset is internally stored as an [`u32`], so you are on
18/// your own if you try to use that crate with a struct larger than 4 GiB.
19///
20/// Offsets can be added together as long as their types correspond, i.e. it is
21/// possible to add an `Offset<B, C>` to an `Offset<A, B>` to get an
22/// `Offset<A, C>`.
23#[repr(transparent)]
24pub struct Offset<Base, Field> {
25    value: u32,
26    marker: marker<*const (Base, Field)>,
27}
28unsafe impl<Base, Field> Send for Offset<Base, Field> {}
29unsafe impl<Base, Field> Sync for Offset<Base, Field> {}
30
31/// Returns a pointer to a field of a base pointer.
32///
33/// The syntax is `field_ptr(base_ptr.field)` where both `base_ptr` and
34/// `field` are idents.
35#[macro_export]
36macro_rules! field_ptr {
37    ($base_ptr:ident.$field:ident) => {
38        // The only valid pointer cast from &T is *const T, so this is always
39        // a const pointer to the field type exactly.
40        &(*$base_ptr).$field as *const _
41    };
42}
43
44/// Constructs an offset from a type and a field name.
45///
46/// The syntax is `offset_of!(Type::field)` where `Type` is either an ident or
47/// a path in angle brackets.
48///
49/// ## Example
50///
51/// ```
52/// struct Hello {
53///     message: &'static str,
54/// }
55///
56/// let hello = Hello {
57///     message: "hello world!",
58/// };
59///
60/// assert!(hello.message == *offset::offset_of!(Hello::message).index_in(&hello));
61/// ```
62#[macro_export]
63macro_rules! offset_of {
64    ($ty:ident::$field:ident) => {
65        $crate::offset_of!(<$ty>::$field)
66    };
67    (<$ty:path>::$field:ident) => {{
68        // This lets us rely on type inference to retrieve the type of the field.
69        #[inline(always)]
70        const unsafe fn offset<Base, Field>(
71            value: u32,
72            _ptr: *const Field,
73        ) -> $crate::Offset<Base, Field> {
74            $crate::Offset::new_unchecked(value)
75        }
76
77        #[allow(unsafe_code)]
78        #[allow(unused_unsafe)]
79        {
80            unsafe {
81                // This asserts that $ty indeed has a field named $field, to be
82                // sure we aren't looking at a field of a Deref target.
83                //
84                // A closure is required to do this because $ty might depend on
85                // type parameters from the environment in which this macro
86                // was used.
87                let _ = |value: &$ty| {
88                    let $ty { $field: _, .. } = *value;
89                };
90
91                let uninit = <core::mem::MaybeUninit<$ty>>::uninit();
92                let base_ptr = uninit.as_ptr();
93                let field_ptr = $crate::field_ptr!(base_ptr.$field);
94                offset::<$ty, _>(
95                    (field_ptr as *const u8).offset_from(base_ptr as *const u8) as u32,
96                    field_ptr,
97                )
98            }
99        }
100    }};
101}
102
103impl<Base, Field> Offset<Base, Field> {
104    /// Creates a new arbitrary offset.
105    ///
106    /// ## Safety
107    ///
108    /// There must be a value of type `Field` at `value` bytes away from
109    /// the start of a value of type `Base`.
110    #[inline(always)]
111    pub const unsafe fn new_unchecked(value: u32) -> Self {
112        Self { value, marker }
113    }
114
115    /// Returns an offset suitable to be used with values of type `MaybeUninit<Base>`.
116    #[inline(always)]
117    pub const fn uninit(self) -> Offset<MaybeUninit<Base>, MaybeUninit<Field>> {
118        Offset {
119            value: self.value,
120            marker,
121        }
122    }
123
124    /// Returns the offset as an [`u32`].
125    ///
126    /// This method provides the same functionality as `From<Self>` for [`u32`]
127    /// but is usable in `const` code.
128    pub const fn as_u32(self) -> u32 {
129        self.value
130    }
131
132    /// Returns a reference to the value of type `Field` in `base` at this offset.
133    #[inline(always)]
134    pub fn index_in(self, base: &Base) -> &Field {
135        unsafe { &*((base as *const Base as *const u8).add(self.value as usize) as *const Field) }
136    }
137
138    /// Returns a mutable reference to the value of type `Field` in `base` at this offset.
139    #[inline(always)]
140    pub fn index_mut_in(self, base: &mut Base) -> &mut Field {
141        unsafe { &mut *((base as *mut Base as *mut u8).add(self.value as usize) as *mut Field) }
142    }
143}
144
145impl<A, B, C> Add<Offset<B, C>> for Offset<A, B> {
146    type Output = Offset<A, C>;
147
148    #[inline(always)]
149    fn add(self, other: Offset<B, C>) -> Self::Output {
150        Offset {
151            value: self.value + other.value,
152            marker,
153        }
154    }
155}
156
157impl<Base, Field> Copy for Offset<Base, Field> {}
158impl<Base, Field> Clone for Offset<Base, Field> {
159    #[inline(always)]
160    fn clone(&self) -> Self {
161        *self
162    }
163}
164
165impl<Base, Field> Eq for Offset<Base, Field> {}
166impl<Base, Field> PartialEq for Offset<Base, Field> {
167    #[inline(always)]
168    fn eq(&self, other: &Self) -> bool {
169        self.value == other.value
170    }
171}
172
173impl<Base, Field> PartialEq<&Self> for Offset<Base, Field> {
174    #[inline(always)]
175    fn eq(&self, other: &&Self) -> bool {
176        self == *other
177    }
178}
179
180impl<Base, Field> PartialEq<u32> for Offset<Base, Field> {
181    #[inline(always)]
182    fn eq(&self, other: &u32) -> bool {
183        self.value == *other
184    }
185}
186
187impl<Base, Field> PartialEq<&u32> for Offset<Base, Field> {
188    #[inline(always)]
189    fn eq(&self, other: &&u32) -> bool {
190        self.value == **other
191    }
192}
193
194impl<Base, Field> Ord for Offset<Base, Field> {
195    #[inline(always)]
196    fn cmp(&self, other: &Self) -> Ordering {
197        self.value.cmp(&other.value)
198    }
199}
200
201impl<Base, Field> PartialOrd for Offset<Base, Field> {
202    #[inline(always)]
203    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
204        Some(self.cmp(other))
205    }
206}
207
208impl<Base, Field> PartialOrd<&Self> for Offset<Base, Field> {
209    #[inline(always)]
210    fn partial_cmp(&self, other: &&Self) -> Option<Ordering> {
211        Some(self.cmp(&**other))
212    }
213}
214
215impl<Base, Field> PartialOrd<u32> for Offset<Base, Field> {
216    #[inline(always)]
217    fn partial_cmp(&self, other: &u32) -> Option<Ordering> {
218        self.value.partial_cmp(other)
219    }
220}
221
222impl<Base, Field> PartialOrd<&u32> for Offset<Base, Field> {
223    #[inline(always)]
224    fn partial_cmp(&self, other: &&u32) -> Option<Ordering> {
225        self.value.partial_cmp(other)
226    }
227}
228
229impl<Base, Field> Hash for Offset<Base, Field> {
230    #[inline(always)]
231    fn hash<H>(&self, state: &mut H)
232    where
233        H: Hasher,
234    {
235        self.value.hash(state);
236    }
237}
238
239impl<Base, Field> fmt::Debug for Offset<Base, Field>
240where
241    Base: DescribeOffset,
242{
243    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
244        Base::describe_offset(self.value, fmt)
245    }
246}
247
248pub trait DescribeOffset {
249    fn describe_offset(offset: u32, fmt: &mut fmt::Formatter) -> fmt::Result;
250}
251
252macro_rules! trivial_fmt_impl {
253    ($($t:ident),*) => {
254        $(impl<Base, Field> fmt::$t for Offset<Base, Field> {
255            fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
256                self.value.fmt(fmt)
257            }
258        })*
259    }
260}
261trivial_fmt_impl!(Binary, Display, LowerHex, Octal, UpperHex);
262
263impl<Base, Field> From<Offset<Base, Field>> for u32 {
264    #[inline(always)]
265    fn from(offset: Offset<Base, Field>) -> Self {
266        offset.value
267    }
268}