gvariant/
offset.rs

1use crate::aligned_bytes::{Alignment, Misaligned, A1, A2, A4, A8};
2use core::{borrow::Borrow, convert::TryFrom, fmt::Display, marker::PhantomData};
3
4/// Represents a usize that is some multiple of [`Alignment::ALIGNMENT`].
5///
6/// `AlignedOffset<A2>` is essentially a usize that is multiple of 2.  This is
7/// useful because you can slice a `AlignedSlice<A2>` and be statically
8/// guaranteed that the slice will still be aligned.
9///
10/// Use `.try_new()`, `<usize>.try_into()` or `align_offset()` to construct
11/// values of this type.
12#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
13pub struct AlignedOffset<A: Alignment>(usize, PhantomData<A>)
14where
15    Self: core::ops::BitOr,
16    <Self as core::ops::BitOr>::Output: Into<Self>;
17impl<A: Alignment> AlignedOffset<A> {
18    /// Convert to usize
19    pub fn to_usize(&self) -> usize {
20        self.0
21    }
22    /// Construct an `AlignedOffset` from a usize.
23    ///
24    /// Result will be `Err(Misaligned)` if the passed `value` is not a multiple
25    /// of the alignment of this type.  This is provided in addition to the
26    /// `TryInto` impl because there is less scope for type ambiguity with this
27    /// function.
28    pub fn try_new(value: usize) -> Result<Self, Misaligned> {
29        if value % A::ALIGNMENT == 0 {
30            Ok(Self(value, PhantomData::<A> {}))
31        } else {
32            Err(Misaligned {})
33        }
34    }
35}
36
37/// Construct an [`AlignedOffset`] by rounding-up `idx` until it's a multiple of
38/// [`A::ALIGNMENT`](Alignment).
39///
40/// This is useful for GVariant deserialisation because often we have an offset
41/// representing the end of a value and we want to find the start of the next
42/// one.  This involves padding according to the next value's alignment.
43pub fn align_offset<A: Alignment>(idx: usize) -> AlignedOffset<A> {
44    AlignedOffset::<A>(
45        (idx + A::ALIGNMENT - 1) & !(A::ALIGNMENT - 1),
46        PhantomData::<A> {},
47    )
48}
49
50impl<A: Alignment> Display for AlignedOffset<A> {
51    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
52        write!(f, "{}", self.0)
53    }
54}
55
56impl<A: Alignment> PartialEq<usize> for AlignedOffset<A> {
57    fn eq(&self, other: &usize) -> bool {
58        *other == self.0
59    }
60}
61impl<A: Alignment> PartialOrd<usize> for AlignedOffset<A> {
62    fn partial_cmp(&self, other: &usize) -> Option<core::cmp::Ordering> {
63        self.0.partial_cmp(other)
64    }
65}
66impl<A: Alignment> PartialEq<AlignedOffset<A>> for usize {
67    fn eq(&self, other: &AlignedOffset<A>) -> bool {
68        *self == other.0
69    }
70}
71impl<A: Alignment> PartialOrd<AlignedOffset<A>> for usize {
72    fn partial_cmp(&self, other: &AlignedOffset<A>) -> Option<core::cmp::Ordering> {
73        self.partial_cmp(&other.0)
74    }
75}
76impl<A: Alignment> TryFrom<usize> for AlignedOffset<A> {
77    type Error = Misaligned;
78    fn try_from(value: usize) -> Result<Self, Self::Error> {
79        Self::try_new(value)
80    }
81}
82impl<A: Alignment> Borrow<usize> for AlignedOffset<A> {
83    fn borrow(&self) -> &usize {
84        &self.0
85    }
86}
87impl<A: Alignment> From<AlignedOffset<A>> for usize {
88    fn from(value: AlignedOffset<A>) -> Self {
89        value.0
90    }
91}
92
93// These are useful for implementing the field alignment algorithm described in
94// the GVariant spec
95impl<A: Alignment> core::ops::BitOr for AlignedOffset<A> {
96    type Output = AlignedOffset<A>;
97    fn bitor(self, rhs: Self) -> Self::Output {
98        // This is safe because neither of A or B will have the bottom bits set,
99        // so we'll end up with a multiple of neither:
100        AlignedOffset::<A>(self.0 | rhs.0, PhantomData::<A> {})
101    }
102}
103
104// These are useful for implementing the field alignment algorithm described in
105// the GVariant spec
106impl<A: Alignment> core::ops::Add for AlignedOffset<A> {
107    type Output = AlignedOffset<A>;
108    fn add(self, rhs: Self) -> Self::Output {
109        // This is safe because neither of A or B will have the bottom bits set,
110        // so we'll end up with a multiple of neither:
111        AlignedOffset::<A>(self.0 + rhs.0, PhantomData::<A> {})
112    }
113}
114
115macro_rules! impl_bitor {
116    ($x:ty, $y:ty, $min:ty) => {
117        impl core::ops::BitOr<AlignedOffset<$x>> for AlignedOffset<$y> {
118            type Output = AlignedOffset<$min>;
119            fn bitor(self, rhs: AlignedOffset<$x>) -> AlignedOffset<$min> {
120                // This is safe because neither of A or B will have the bottom bits set,
121                // so we'll end up with a multiple of neither:
122                AlignedOffset::<$min>(self.0 | rhs.0, PhantomData::<$min> {})
123            }
124        }
125    };
126}
127macro_rules! narrowing {
128    ($big:ty, $small:ty) => {
129        impl_bitor!($big, $small, $small);
130        impl_bitor!($small, $big, $small);
131        impl From<AlignedOffset<$big>> for AlignedOffset<$small> {
132            fn from(v: AlignedOffset<$big>) -> Self {
133                AlignedOffset::<$small>(v.0, PhantomData)
134            }
135        }
136    };
137}
138
139narrowing!(A2, A1);
140narrowing!(A4, A1);
141narrowing!(A8, A1);
142narrowing!(A4, A2);
143narrowing!(A8, A2);
144narrowing!(A8, A4);
145
146#[cfg(test)]
147#[cfg(feature = "alloc")]
148mod tests {
149    use super::*;
150    use crate::aligned_bytes::{A2, A4};
151    use core::convert::TryInto;
152
153    #[test]
154    fn test() {
155        let x: Result<AlignedOffset<A2>, Misaligned> = 3usize.try_into();
156        assert!(x.is_err());
157
158        let x: AlignedOffset<A2> = 0usize.try_into().unwrap();
159        assert_eq!(x.to_usize(), 0usize);
160
161        let x: AlignedOffset<A4> = 8usize.try_into().unwrap();
162        assert_eq!(x.to_usize(), 8usize);
163
164        assert_eq!(align_offset::<A4>(0).to_usize(), 0);
165        assert_eq!(align_offset::<A4>(1).to_usize(), 4);
166        assert_eq!(align_offset::<A4>(4).to_usize(), 4);
167        assert_eq!(align_offset::<A4>(6).to_usize(), 8);
168        assert_eq!(align_offset::<A4>(7).to_usize(), 8);
169        assert_eq!(align_offset::<A4>(8).to_usize(), 8);
170        assert_eq!(align_offset::<A4>(9).to_usize(), 12);
171    }
172}