musli_zerocopy/endian/
endian.rs

1use core::marker::PhantomData;
2use core::ops::{Deref, DerefMut};
3use core::{any, fmt};
4
5use crate::ZeroCopy;
6use crate::endian::{Big, ByteOrder, Little, Native};
7
8/// Wrapper capable of enforcing a custom [`ByteOrder`].
9///
10/// This can be used to store values in a zero-copy container in a portable
11/// manner, which is especially important to transfer types such as `char` which
12/// have a limited supported bit-pattern.
13///
14/// # Wrapping fields
15///
16/// Any type which implements [`ZeroCopy`] and has [`ZeroCopy::CAN_SWAP_BYTES`]
17/// set to `true` can be portably wrapped with this type.
18///
19/// [`ZeroCopy::CAN_SWAP_BYTES`] is not `true` it indicates that the type
20/// contains data which cannot be safely byte-swapped, such as [`char`]. Byte
21/// swapping such a type is a no-op and will return the original type.
22///
23/// ```
24/// use musli_zerocopy::{endian, Endian, OwnedBuf, Ref, ZeroCopy};
25///
26/// #[derive(Clone, Copy, ZeroCopy)]
27/// #[repr(C)]
28/// struct Struct {
29///     name: Ref<str>,
30///     age: Endian<u32, endian::Big>,
31/// }
32///
33/// let mut buf = OwnedBuf::new();
34///
35/// let name = buf.store_unsized("John");
36///
37/// let data = buf.store(&Struct {
38///     name,
39///     age: Endian::new(35),
40/// });
41///
42/// buf.align_in_place();
43///
44/// let data = buf.load(data)?;
45///
46/// assert_eq!(buf.load(data.name)?, "John");
47/// assert_eq!(data.age.to_ne(), 35);
48/// # Ok::<_, musli_zerocopy::Error>(())
49/// ```
50#[derive(ZeroCopy)]
51#[zero_copy(crate, swap_bytes_self, bounds = {T: ZeroCopy})]
52#[repr(transparent)]
53pub struct Endian<T, E>
54where
55    E: ByteOrder,
56{
57    value: T,
58    #[zero_copy(ignore)]
59    _marker: PhantomData<E>,
60}
61
62impl<T> Endian<T, Little>
63where
64    T: ZeroCopy,
65{
66    /// Construct new value wrapper with [`Little`] [`ByteOrder`].
67    ///
68    /// # Examples
69    ///
70    /// ```
71    /// use musli_zerocopy::Endian;
72    ///
73    /// let value = Endian::le(42u32);
74    /// assert_eq!(value.to_ne(), 42);
75    /// assert_eq!(value.to_raw(), 42u32.to_le());
76    /// ```
77    #[inline]
78    pub fn le(value: T) -> Self {
79        Self::new(value)
80    }
81}
82
83impl<T> Endian<T, Big>
84where
85    T: ZeroCopy,
86{
87    /// Construct new value wrapper with [`Big`] [`ByteOrder`].
88    ///
89    /// # Examples
90    ///
91    /// ```
92    /// use musli_zerocopy::Endian;
93    ///
94    /// let value = Endian::be(42u32);
95    /// assert_eq!(value.to_ne(), 42);
96    /// assert_eq!(value.to_raw(), 42u32.to_be());
97    /// ```
98    #[inline]
99    pub fn be(value: T) -> Self {
100        Self::new(value)
101    }
102}
103
104impl<T, E> Endian<T, E>
105where
106    T: ZeroCopy,
107    E: ByteOrder,
108{
109    /// Construct new value wrapper with the specified [`ByteOrder`].
110    ///
111    /// # Panics
112    ///
113    /// Panics if we try to use this with a `ZeroCopy` type that cannot be
114    /// byte-ordered.
115    ///
116    /// ```compile_fail
117    /// use musli_zerocopy::{endian, Endian};
118    ///
119    /// let _: Endian<_, endian::Little> = Endian::new('a');
120    /// ```
121    ///
122    /// # Examples
123    ///
124    /// ```
125    /// use musli_zerocopy::{endian, Endian, ZeroCopy};
126    ///
127    /// let mut a: Endian<_, endian::Big> = Endian::new('a' as u32);
128    /// let mut b: Endian<_, endian::Little> = Endian::new('a' as u32);
129    ///
130    /// assert_eq!(a.to_ne(), 'a' as u32);
131    /// assert_eq!(a.to_bytes(), &[0, 0, 0, 97]);
132    ///
133    /// assert_eq!(b.to_ne(), 'a' as u32);
134    /// assert_eq!(b.to_bytes(), &[97, 0, 0, 0]);
135    /// ```
136    #[inline]
137    #[track_caller]
138    pub fn new(value: T) -> Self {
139        const {
140            assert!(T::CAN_SWAP_BYTES, "Type does not support byte-swapping");
141        }
142
143        Self {
144            value: T::swap_bytes::<E>(value),
145            _marker: PhantomData,
146        }
147    }
148
149    /// Get interior value in native [`ByteOrder`].
150    ///
151    /// # Examples
152    ///
153    /// ```
154    /// use musli_zerocopy::Endian;
155    ///
156    /// let value = Endian::le(42u32);
157    /// assert_eq!(value.to_ne(), 42);
158    /// assert_eq!(value.to_raw(), 42u32.to_le());
159    /// ```
160    #[inline]
161    #[track_caller]
162    pub fn to_ne(self) -> T {
163        const {
164            assert!(T::CAN_SWAP_BYTES, "Type does not support byte-swapping");
165        }
166
167        T::swap_bytes::<E>(self.value)
168    }
169
170    /// Get the raw inner value.
171    ///
172    /// # Examples
173    ///
174    /// ```
175    /// use musli_zerocopy::Endian;
176    ///
177    /// let value = Endian::le(42u32);
178    /// assert_eq!(value.to_ne(), 42);
179    /// assert_eq!(value.to_raw(), 42u32.to_le());
180    /// ```
181    #[inline]
182    pub fn to_raw(self) -> T {
183        self.value
184    }
185}
186
187impl<T, E> fmt::Debug for Endian<T, E>
188where
189    T: ZeroCopy + fmt::Debug,
190    E: ByteOrder,
191{
192    #[inline]
193    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
194        write!(f, "Endian<{}>({:?})", any::type_name::<E>(), self.value)
195    }
196}
197
198impl<T, E> Clone for Endian<T, E>
199where
200    T: ZeroCopy + Clone,
201    E: ByteOrder,
202{
203    fn clone(&self) -> Self {
204        Self {
205            value: self.value.clone(),
206            _marker: self._marker,
207        }
208    }
209}
210
211impl<T, E> Copy for Endian<T, E>
212where
213    T: ZeroCopy + Copy,
214    E: ByteOrder,
215{
216}
217
218/// Any `Endian<T>` implements [`Deref<Target = T>`] for natively wrapped types.
219///
220/// # Examples
221///
222/// ```
223/// use musli_zerocopy::Endian;
224///
225/// let value = Endian::new(42u32);
226/// assert_eq!(*value, 42u32);
227/// ```
228impl<T> Deref for Endian<T, Native> {
229    type Target = T;
230
231    #[inline]
232    fn deref(&self) -> &Self::Target {
233        &self.value
234    }
235}
236
237/// Any `Endian<T>` implements [`DerefMut<Target = T>`] for natively wrapped types.
238///
239/// # Examples
240///
241/// ```
242/// use musli_zerocopy::Endian;
243///
244/// let mut value = Endian::new(42u32);
245/// assert_eq!(*value, 42u32);
246/// *value += 1;
247/// assert_eq!(*value, 43u32);
248/// ```
249impl<T> DerefMut for Endian<T, Native> {
250    #[inline]
251    fn deref_mut(&mut self) -> &mut Self::Target {
252        &mut self.value
253    }
254}