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    unsafe fn parse_step<const ALIGNED: bool>(bytes: &[u8]) -> Option<(&Unaligned<Self>, &[u8])> {
64        // SAFETY: The unaligned flavour has no requirements. It is used even if `ALIGNED`, since
65        // `Self` has alignment 1 and `bytes` aren't necessarily aligned for `Data` then.
66        let (_value, remainder) = unsafe { Data::parse_step::<false>(bytes) }?;
67        // SAFETY: `bytes` start with a valid value of `Data` (parsing step contract), which has the
68        // same bytes and validity as `Self`, `Unaligned<Self>` has alignment 1 and the size of
69        // `Self`
70        let value = unsafe { bytes.as_ptr().cast::<Unaligned<Self>>().as_ref_unchecked() };
71        Some((value, remainder))
72    }
73}
74
75// SAFETY: Same bytes and validity as `Data`, the parsing step parses `Data`
76unsafe impl<Data> AnyBitPatternMarker for Unaligned<Data> where
77    Data: TrivialType + AnyBitPatternMarker
78{
79}
80
81#[cfg(feature = "scale-codec")]
82impl<Data> Encode for Unaligned<Data>
83where
84    Data: TrivialType + Encode,
85{
86    #[inline(always)]
87    fn size_hint(&self) -> usize {
88        let inner = self.0;
89        inner.size_hint()
90    }
91
92    #[inline(always)]
93    fn encode_to<T>(&self, dest: &mut T)
94    where
95        T: Output + ?Sized,
96    {
97        let inner = self.0;
98        inner.encode_to(dest);
99    }
100
101    #[inline(always)]
102    fn using_encoded<R, F>(&self, f: F) -> R
103    where
104        F: FnOnce(&[u8]) -> R,
105    {
106        let inner = self.0;
107        inner.using_encoded(f)
108    }
109}
110
111#[cfg(feature = "scale-codec")]
112impl<Data> EncodeLike for Unaligned<Data> where Data: TrivialType + Encode {}
113
114#[cfg(feature = "serde")]
115impl<Data> Serialize for Unaligned<Data>
116where
117    Data: TrivialType + Serialize,
118{
119    #[inline(always)]
120    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
121    where
122        S: Serializer,
123    {
124        let inner = self.0;
125        inner.serialize(serializer)
126    }
127}
128
129impl<Data> Unaligned<Data>
130where
131    Data: TrivialType,
132{
133    /// Create a new instance
134    #[inline(always)]
135    pub const fn new(inner: Data) -> Self {
136        Self(inner)
137    }
138
139    /// Get inner value
140    #[inline(always)]
141    pub const fn as_inner(&self) -> Data {
142        self.0
143    }
144
145    /// Replace inner value
146    pub const fn replace(&mut self, value: Data) {
147        self.0 = value;
148    }
149}