1use core::mem::{size_of, transmute_copy};
53use zerofrom::ZeroFrom;
54
55use super::{AsULE, EncodeAsVarULE, UleError, VarULE, ULE};
56
57#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
61#[allow(clippy::exhaustive_structs)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
63pub struct VarTuple<A, B> {
64 pub sized: A,
65 pub variable: B,
66}
67
68#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
72#[allow(clippy::exhaustive_structs)] #[repr(C)]
74pub struct VarTupleULE<A: AsULE, V: VarULE + ?Sized> {
75 pub sized: A::ULE,
76 pub variable: V,
77}
78
79unsafe impl<A, V> VarULE for VarTupleULE<A, V>
107where
108 A: AsULE + 'static,
109 V: VarULE + ?Sized,
110{
111 fn validate_bytes(bytes: &[u8]) -> Result<(), UleError> {
112 let (sized_chunk, variable_chunk) = bytes
113 .split_at_checked(size_of::<A::ULE>())
114 .ok_or(UleError::length::<Self>(bytes.len()))?;
115 A::ULE::validate_bytes(sized_chunk)?;
116 V::validate_bytes(variable_chunk)?;
117 Ok(())
118 }
119
120 unsafe fn from_bytes_unchecked(bytes: &[u8]) -> &Self {
121 let (_sized_chunk, variable_chunk) = bytes.split_at_unchecked(size_of::<A::ULE>());
122 let variable_ref = V::from_bytes_unchecked(variable_chunk);
125 let variable_ptr: *const V = variable_ref;
126
127 assert_eq!(size_of::<*const V>(), size_of::<(*const u8, usize)>());
136 let (_v_ptr, metadata) = transmute_copy::<*const V, (*const u8, usize)>(&variable_ptr);
139
140 assert_eq!(size_of::<*const Self>(), size_of::<(*const u8, usize)>());
142 let composed_ptr =
144 transmute_copy::<(*const u8, usize), *const Self>(&(bytes.as_ptr(), metadata));
145 &*(composed_ptr)
146 }
147}
148
149unsafe impl<A, B, V> EncodeAsVarULE<VarTupleULE<A, V>> for VarTuple<A, B>
155where
156 A: AsULE + 'static,
157 B: EncodeAsVarULE<V>,
158 V: VarULE + ?Sized,
159{
160 fn encode_var_ule_as_slices<R>(&self, _: impl FnOnce(&[&[u8]]) -> R) -> R {
161 unreachable!()
163 }
164
165 #[inline]
166 fn encode_var_ule_len(&self) -> usize {
167 size_of::<A::ULE>() + self.variable.encode_var_ule_len()
168 }
169
170 #[inline]
171 fn encode_var_ule_write(&self, dst: &mut [u8]) {
172 let (sized_chunk, variable_chunk) = dst.split_at_mut(size_of::<A::ULE>());
174 sized_chunk.clone_from_slice([self.sized.to_unaligned()].as_bytes());
175 self.variable.encode_var_ule_write(variable_chunk);
176 }
177}
178
179#[cfg(feature = "alloc")]
180impl<A, V> alloc::borrow::ToOwned for VarTupleULE<A, V>
181where
182 A: AsULE + 'static,
183 V: VarULE + ?Sized,
184{
185 type Owned = alloc::boxed::Box<Self>;
186 fn to_owned(&self) -> Self::Owned {
187 crate::ule::encode_varule_to_box(self)
188 }
189}
190
191impl<'a, A, B, V> ZeroFrom<'a, VarTupleULE<A, V>> for VarTuple<A, B>
192where
193 A: AsULE + 'static,
194 V: VarULE + ?Sized,
195 B: ZeroFrom<'a, V>,
196{
197 fn zero_from(other: &'a VarTupleULE<A, V>) -> Self {
198 VarTuple {
199 sized: AsULE::from_unaligned(other.sized),
200 variable: B::zero_from(&other.variable),
201 }
202 }
203}
204
205#[cfg(feature = "serde")]
206impl<A, V> serde::Serialize for VarTupleULE<A, V>
207where
208 A: AsULE + 'static,
209 V: VarULE + ?Sized,
210 A: serde::Serialize,
211 V: serde::Serialize,
212{
213 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
214 where
215 S: serde::Serializer,
216 {
217 if serializer.is_human_readable() {
218 let this = VarTuple {
219 sized: A::from_unaligned(self.sized),
220 variable: &self.variable,
221 };
222 this.serialize(serializer)
223 } else {
224 serializer.serialize_bytes(self.as_bytes())
225 }
226 }
227}
228
229#[cfg(feature = "serde")]
230impl<'a, 'de: 'a, A, V> serde::Deserialize<'de> for &'a VarTupleULE<A, V>
231where
232 A: AsULE + 'static,
233 V: VarULE + ?Sized,
234 A: serde::Deserialize<'de>,
235{
236 fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error>
237 where
238 Des: serde::Deserializer<'de>,
239 {
240 if !deserializer.is_human_readable() {
241 let bytes = <&[u8]>::deserialize(deserializer)?;
242 VarTupleULE::<A, V>::parse_bytes(bytes).map_err(serde::de::Error::custom)
243 } else {
244 Err(serde::de::Error::custom(
245 "&VarTupleULE can only deserialize in zero-copy ways",
246 ))
247 }
248 }
249}
250
251#[cfg(feature = "serde")]
252impl<'de, A, V> serde::Deserialize<'de> for alloc::boxed::Box<VarTupleULE<A, V>>
253where
254 A: AsULE + 'static,
255 V: VarULE + ?Sized,
256 A: serde::Deserialize<'de>,
257 alloc::boxed::Box<V>: serde::Deserialize<'de>,
258{
259 fn deserialize<Des>(deserializer: Des) -> Result<Self, Des::Error>
260 where
261 Des: serde::Deserializer<'de>,
262 {
263 if deserializer.is_human_readable() {
264 let this = VarTuple::<A, alloc::boxed::Box<V>>::deserialize(deserializer)?;
265 Ok(crate::ule::encode_varule_to_box(&this))
266 } else {
267 let deserialized = <&VarTupleULE<A, V>>::deserialize(deserializer)?;
270 Ok(deserialized.to_boxed())
271 }
272 }
273}
274
275#[test]
276fn test_simple() {
277 let var_tuple = VarTuple {
278 sized: 1500u16,
279 variable: "hello",
280 };
281 let var_tuple_ule = super::encode_varule_to_box(&var_tuple);
282 assert_eq!(var_tuple_ule.sized.as_unsigned_int(), 1500);
283 assert_eq!(&var_tuple_ule.variable, "hello");
284
285 #[cfg(feature = "serde")]
287 crate::ule::test_utils::assert_serde_roundtrips::<VarTupleULE<u16, str>>(&var_tuple_ule);
288}
289
290#[test]
291fn test_nested() {
292 use crate::{ZeroSlice, ZeroVec};
293 let var_tuple = VarTuple {
294 sized: 2000u16,
295 variable: VarTuple {
296 sized: '🦙',
297 variable: ZeroVec::alloc_from_slice(b"ICU"),
298 },
299 };
300 let var_tuple_ule = super::encode_varule_to_box(&var_tuple);
301 assert_eq!(var_tuple_ule.sized.as_unsigned_int(), 2000u16);
302 assert_eq!(var_tuple_ule.variable.sized.to_char(), '🦙');
303 assert_eq!(
304 &var_tuple_ule.variable.variable,
305 ZeroSlice::from_ule_slice(b"ICU")
306 );
307 #[cfg(feature = "serde")]
309 crate::ule::test_utils::assert_serde_roundtrips::<
310 VarTupleULE<u16, VarTupleULE<char, ZeroSlice<_>>>,
311 >(&var_tuple_ule);
312}