Skip to main content

ab_io_type/
unaligned.rs

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