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}