Skip to main content

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/// let data = buf.store(&Struct {
37///     name,
38///     age: Endian::new(35),
39/// })?;
40///
41/// buf.align_in_place()?;
42///
43/// let data = buf.load(data)?;
44///
45/// assert_eq!(buf.load(data.name)?, "John");
46/// assert_eq!(data.age.to_ne(), 35);
47/// # Ok::<_, musli_zerocopy::Error>(())
48/// ```
49#[derive(ZeroCopy)]
50#[zero_copy(crate, swap_bytes_self, bounds = {T: ZeroCopy})]
51#[repr(transparent)]
52pub struct Endian<T, E>
53where
54    E: ByteOrder,
55{
56    value: T,
57    #[zero_copy(ignore)]
58    _marker: PhantomData<E>,
59}
60
61impl<T> Endian<T, Little>
62where
63    T: ZeroCopy,
64{
65    /// Construct new value wrapper with [`Little`] [`ByteOrder`].
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use musli_zerocopy::Endian;
71    ///
72    /// let value = Endian::le(42u32);
73    /// assert_eq!(value.to_ne(), 42);
74    /// assert_eq!(value.to_raw(), 42u32.to_le());
75    /// ```
76    #[inline]
77    pub fn le(value: T) -> Self {
78        Self::new(value)
79    }
80}
81
82impl<T> Endian<T, Big>
83where
84    T: ZeroCopy,
85{
86    /// Construct new value wrapper with [`Big`] [`ByteOrder`].
87    ///
88    /// # Examples
89    ///
90    /// ```
91    /// use musli_zerocopy::Endian;
92    ///
93    /// let value = Endian::be(42u32);
94    /// assert_eq!(value.to_ne(), 42);
95    /// assert_eq!(value.to_raw(), 42u32.to_be());
96    /// ```
97    #[inline]
98    pub fn be(value: T) -> Self {
99        Self::new(value)
100    }
101}
102
103impl<T, E> Endian<T, E>
104where
105    T: ZeroCopy,
106    E: ByteOrder,
107{
108    /// Construct new value wrapper with the specified [`ByteOrder`].
109    ///
110    /// # Panics
111    ///
112    /// Panics if we try to use this with a `ZeroCopy` type that cannot be
113    /// byte-ordered.
114    ///
115    /// ```compile_fail
116    /// use musli_zerocopy::{endian, Endian};
117    ///
118    /// let _: Endian<_, endian::Little> = Endian::new('a');
119    /// ```
120    ///
121    /// # Examples
122    ///
123    /// ```
124    /// use musli_zerocopy::{endian, Endian, ZeroCopy};
125    ///
126    /// let mut a: Endian<_, endian::Big> = Endian::new('a' as u32);
127    /// let mut b: Endian<_, endian::Little> = Endian::new('a' as u32);
128    ///
129    /// assert_eq!(a.to_ne(), 'a' as u32);
130    /// assert_eq!(a.to_bytes(), &[0, 0, 0, 97]);
131    ///
132    /// assert_eq!(b.to_ne(), 'a' as u32);
133    /// assert_eq!(b.to_bytes(), &[97, 0, 0, 0]);
134    /// ```
135    #[inline]
136    #[track_caller]
137    pub fn new(value: T) -> Self {
138        const {
139            assert!(T::CAN_SWAP_BYTES, "Type does not support byte-swapping");
140        }
141
142        Self {
143            value: T::swap_bytes::<E>(value),
144            _marker: PhantomData,
145        }
146    }
147
148    /// Get interior value in native [`ByteOrder`].
149    ///
150    /// # Examples
151    ///
152    /// ```
153    /// use musli_zerocopy::Endian;
154    ///
155    /// let value = Endian::le(42u32);
156    /// assert_eq!(value.to_ne(), 42);
157    /// assert_eq!(value.to_raw(), 42u32.to_le());
158    /// ```
159    #[inline]
160    #[track_caller]
161    pub fn to_ne(self) -> T {
162        const {
163            assert!(T::CAN_SWAP_BYTES, "Type does not support byte-swapping");
164        }
165
166        T::swap_bytes::<E>(self.value)
167    }
168
169    /// Get the raw inner value.
170    ///
171    /// # Examples
172    ///
173    /// ```
174    /// use musli_zerocopy::Endian;
175    ///
176    /// let value = Endian::le(42u32);
177    /// assert_eq!(value.to_ne(), 42);
178    /// assert_eq!(value.to_raw(), 42u32.to_le());
179    /// ```
180    #[inline]
181    pub fn to_raw(self) -> T {
182        self.value
183    }
184}
185
186impl<T, E> fmt::Debug for Endian<T, E>
187where
188    T: ZeroCopy + fmt::Debug,
189    E: ByteOrder,
190{
191    #[inline]
192    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
193        write!(f, "Endian<{}>({:?})", any::type_name::<E>(), self.value)
194    }
195}
196
197impl<T, E> Clone for Endian<T, E>
198where
199    T: ZeroCopy + Clone,
200    E: ByteOrder,
201{
202    fn clone(&self) -> Self {
203        Self {
204            value: self.value.clone(),
205            _marker: self._marker,
206        }
207    }
208}
209
210impl<T, E> Copy for Endian<T, E>
211where
212    T: ZeroCopy + Copy,
213    E: ByteOrder,
214{
215}
216
217/// Any `Endian<T>` implements [`Deref<Target = T>`] for natively wrapped types.
218///
219/// # Examples
220///
221/// ```
222/// use musli_zerocopy::Endian;
223///
224/// let value = Endian::new(42u32);
225/// assert_eq!(*value, 42u32);
226/// ```
227impl<T> Deref for Endian<T, Native> {
228    type Target = T;
229
230    #[inline]
231    fn deref(&self) -> &Self::Target {
232        &self.value
233    }
234}
235
236/// Any `Endian<T>` implements [`DerefMut<Target = T>`] for natively wrapped types.
237///
238/// # Examples
239///
240/// ```
241/// use musli_zerocopy::Endian;
242///
243/// let mut value = Endian::new(42u32);
244/// assert_eq!(*value, 42u32);
245/// *value += 1;
246/// assert_eq!(*value, 43u32);
247/// ```
248impl<T> DerefMut for Endian<T, Native> {
249    #[inline]
250    fn deref_mut(&mut self) -> &mut Self::Target {
251        &mut self.value
252    }
253}