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