Skip to main content

ab_io_type/
unaligned.rs

1use crate::alignment::Alignment;
2use crate::metadata::{IoTypeMetadataKind, MAX_METADATA_CAPACITY, concat_metadata_sources};
3use crate::trivial_type::{AnyBitPatternMarker, TrivialType};
4use core::fmt;
5#[cfg(feature = "scale-codec")]
6use parity_scale_codec::{Decode, Encode, EncodeLike, MaxEncodedLen, Output};
7#[cfg(feature = "serde")]
8use serde::{Deserialize, Serialize, Serializer};
9
10/// Wrapper type for `Data` that is the same size, but doesn't need to be aligned/has alignment of
11/// one byte.
12///
13/// This is similar to `#[repr(packed)]`, but makes sure to only expose safe API when dealing with
14/// the contents. For example, if `Data` is unaligned and has fields, it is not sounds having
15/// references to its fields. This data structure prevents such invalid invariants.
16#[derive(Debug, Default, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
17#[cfg_attr(feature = "scale-codec", derive(Decode, MaxEncodedLen))]
18#[cfg_attr(feature = "serde", derive(Deserialize))]
19#[cfg_attr(feature = "serde", serde(transparent))]
20#[repr(C, packed)]
21pub struct Unaligned<Data>(Data)
22where
23    Data: TrivialType;
24
25const impl<Data> From<Data> for Unaligned<Data>
26where
27    Data: TrivialType,
28{
29    #[inline(always)]
30    fn from(value: Data) -> Self {
31        Self(value)
32    }
33}
34
35impl<Data> fmt::Display for Unaligned<Data>
36where
37    Data: TrivialType + fmt::Display,
38{
39    #[inline]
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        let inner = self.0;
42        inner.fmt(f)
43    }
44}
45
46// SAFETY: Same bytes and validity as `Data`, the parsing step parses `Data`
47unsafe impl<Data> TrivialType for Unaligned<Data>
48where
49    Data: TrivialType,
50{
51    const ALIGNMENT: Alignment = u8::ALIGNMENT;
52    const METADATA: &[u8] = {
53        const fn metadata(inner_metadata: &[u8]) -> ([u8; MAX_METADATA_CAPACITY], usize) {
54            concat_metadata_sources(&[&[IoTypeMetadataKind::Unaligned as u8], inner_metadata])
55        }
56
57        // The reference extends the lifetime of metadata scratch to that of the constant
58        let (metadata_scratch, metadata_size) = &metadata(Data::METADATA);
59        metadata_scratch.split_at(*metadata_size).0
60    };
61
62    #[inline(always)]
63    fn try_from_bytes_unaligned(bytes: &[u8]) -> Option<(&Unaligned<Self>, &[u8])> {
64        let (_value, remainder) = Data::try_from_bytes_unaligned(bytes)?;
65        // SAFETY: `bytes` start with a valid value of `Data` (parsing step contract), which has the
66        // same bytes and validity as `Self`, `Unaligned<Self>` has alignment 1 and the size of
67        // `Self`
68        let value = unsafe { bytes.as_ptr().cast::<Unaligned<Self>>().as_ref_unchecked() };
69        Some((value, remainder))
70    }
71}
72
73// SAFETY: Same bytes and validity as `Data`, the parsing step parses `Data`
74unsafe impl<Data> AnyBitPatternMarker for Unaligned<Data> where
75    Data: TrivialType + AnyBitPatternMarker
76{
77}
78
79#[cfg(feature = "scale-codec")]
80impl<Data> Encode for Unaligned<Data>
81where
82    Data: TrivialType + Encode,
83{
84    #[inline(always)]
85    fn size_hint(&self) -> usize {
86        let inner = self.0;
87        inner.size_hint()
88    }
89
90    #[inline(always)]
91    fn encode_to<T>(&self, dest: &mut T)
92    where
93        T: Output + ?Sized,
94    {
95        let inner = self.0;
96        inner.encode_to(dest);
97    }
98
99    #[inline(always)]
100    fn using_encoded<R, F>(&self, f: F) -> R
101    where
102        F: FnOnce(&[u8]) -> R,
103    {
104        let inner = self.0;
105        inner.using_encoded(f)
106    }
107}
108
109#[cfg(feature = "scale-codec")]
110impl<Data> EncodeLike for Unaligned<Data> where Data: TrivialType + Encode {}
111
112#[cfg(feature = "serde")]
113impl<Data> Serialize for Unaligned<Data>
114where
115    Data: TrivialType + Serialize,
116{
117    #[inline(always)]
118    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
119    where
120        S: Serializer,
121    {
122        let inner = self.0;
123        inner.serialize(serializer)
124    }
125}
126
127impl<Data> Unaligned<Data>
128where
129    Data: TrivialType,
130{
131    /// Create a new instance
132    #[inline(always)]
133    pub const fn new(inner: Data) -> Self {
134        Self(inner)
135    }
136
137    /// Get inner value
138    #[inline(always)]
139    pub const fn as_inner(&self) -> Data {
140        self.0
141    }
142
143    /// Replace inner value
144    pub const fn replace(&mut self, value: Data) {
145        self.0 = value;
146    }
147}