1mod error;
8
9use core::marker::PhantomData;
10use core::mem::MaybeUninit;
11use core::ptr::copy_nonoverlapping;
12
13pub use self::error::EncodeError;
14
15use crate::{
16 Encoder, EncoderExt as _, WireBox, WireF32, WireF64, WireI16, WireI32, WireI64, WireU16,
17 WireU32, WireU64,
18};
19
20pub struct CopyOptimization<T: ?Sized>(bool, PhantomData<T>);
22
23impl<T: ?Sized> CopyOptimization<T> {
24 pub const unsafe fn enable() -> Self {
30 Self(true, PhantomData)
31 }
32
33 pub const unsafe fn enable_if(value: bool) -> Self {
40 Self(value, PhantomData)
41 }
42
43 pub const fn disable() -> Self {
45 Self(false, PhantomData)
46 }
47
48 pub const fn is_enabled(&self) -> bool {
50 self.0
51 }
52}
53
54pub unsafe trait ZeroPadding: Sized {
60 fn zero_padding(out: &mut MaybeUninit<Self>);
62}
63
64unsafe impl<T: ZeroPadding, const N: usize> ZeroPadding for [T; N] {
65 #[inline]
66 fn zero_padding(out: &mut MaybeUninit<Self>) {
67 for i in 0..N {
68 let out_i = unsafe { &mut *out.as_mut_ptr().cast::<MaybeUninit<T>>().add(i) };
69 T::zero_padding(out_i);
70 }
71 }
72}
73
74macro_rules! impl_zero_padding_for_primitive {
75 ($ty:ty) => {
76 unsafe impl ZeroPadding for $ty {
77 #[inline]
78 fn zero_padding(_: &mut MaybeUninit<Self>) {}
79 }
80 };
81}
82
83macro_rules! impl_zero_padding_for_primitives {
84 ($($ty:ty),* $(,)?) => {
85 $(
86 impl_zero_padding_for_primitive!($ty);
87 )*
88 }
89}
90
91impl_zero_padding_for_primitives! {
92 (), bool, i8, u8,
93 WireI16, WireI32, WireI64,
94 WireU16, WireU32, WireU64,
95 WireF32, WireF64,
96}
97
98pub trait Encodable {
100 const COPY_OPTIMIZATION: CopyOptimization<Self> = CopyOptimization::disable();
107
108 type Encoded: ZeroPadding;
110}
111
112pub unsafe trait Encode<E: ?Sized>: Encodable + Sized {
118 fn encode(
120 self,
121 encoder: &mut E,
122 out: &mut MaybeUninit<Self::Encoded>,
123 ) -> Result<(), EncodeError>;
124}
125
126pub unsafe trait EncodeRef<E: ?Sized>: Encode<E> {
132 fn encode_ref(
134 &self,
135 encoder: &mut E,
136 out: &mut MaybeUninit<Self::Encoded>,
137 ) -> Result<(), EncodeError>;
138}
139
140pub trait EncodableOption {
142 type EncodedOption: ZeroPadding;
144}
145
146pub unsafe trait EncodeOption<E: ?Sized>: EncodableOption + Sized {
152 fn encode_option(
154 this: Option<Self>,
155 encoder: &mut E,
156 out: &mut MaybeUninit<Self::EncodedOption>,
157 ) -> Result<(), EncodeError>;
158}
159
160pub unsafe trait EncodeOptionRef<E: ?Sized>: EncodeOption<E> {
166 fn encode_option_ref(
168 this: Option<&Self>,
169 encoder: &mut E,
170 out: &mut MaybeUninit<Self::EncodedOption>,
171 ) -> Result<(), EncodeError>;
172}
173
174impl<T: Encodable> Encodable for &T {
175 type Encoded = T::Encoded;
176}
177
178unsafe impl<E: ?Sized, T: EncodeRef<E>> Encode<E> for &T {
179 fn encode(
180 self,
181 encoder: &mut E,
182 out: &mut MaybeUninit<Self::Encoded>,
183 ) -> Result<(), EncodeError> {
184 T::encode_ref(self, encoder, out)
185 }
186}
187
188impl<T: EncodableOption> EncodableOption for &T {
189 type EncodedOption = T::EncodedOption;
190}
191
192unsafe impl<E: ?Sized, T: EncodeOptionRef<E>> EncodeOption<E> for &T {
193 fn encode_option(
194 this: Option<Self>,
195 encoder: &mut E,
196 out: &mut MaybeUninit<Self::EncodedOption>,
197 ) -> Result<(), EncodeError> {
198 T::encode_option_ref(this, encoder, out)
199 }
200}
201
202macro_rules! impl_encode_for_primitive {
203 ($ty:ty, $enc:ty) => {
204 impl Encodable for $ty {
205 const COPY_OPTIMIZATION: CopyOptimization<Self> = unsafe {
208 CopyOptimization::enable_if(
209 size_of::<Self>() <= 1 || cfg!(target_endian = "little"),
210 )
211 };
212
213 type Encoded = $enc;
214 }
215
216 unsafe impl<E: ?Sized> Encode<E> for $ty {
217 #[inline]
218 fn encode(
219 self,
220 encoder: &mut E,
221 out: &mut MaybeUninit<Self::Encoded>,
222 ) -> Result<(), EncodeError> {
223 self.encode_ref(encoder, out)
224 }
225 }
226
227 unsafe impl<E: ?Sized> EncodeRef<E> for $ty {
228 #[inline]
229 fn encode_ref(
230 &self,
231 _: &mut E,
232 out: &mut MaybeUninit<Self::Encoded>,
233 ) -> Result<(), EncodeError> {
234 out.write(<$enc>::from(*self));
235 Ok(())
236 }
237 }
238
239 impl EncodableOption for $ty {
240 type EncodedOption = WireBox<$enc>;
241 }
242
243 unsafe impl<E: Encoder + ?Sized> EncodeOption<E> for $ty {
244 #[inline]
245 fn encode_option(
246 this: Option<Self>,
247 encoder: &mut E,
248 out: &mut MaybeUninit<Self::EncodedOption>,
249 ) -> Result<(), EncodeError> {
250 Self::encode_option_ref(this.as_ref(), encoder, out)
251 }
252 }
253
254 unsafe impl<E: Encoder + ?Sized> EncodeOptionRef<E> for $ty {
255 #[inline]
256 fn encode_option_ref(
257 this: Option<&Self>,
258 encoder: &mut E,
259 out: &mut MaybeUninit<Self::EncodedOption>,
260 ) -> Result<(), EncodeError> {
261 if let Some(value) = this {
262 encoder.encode_next(value)?;
263 WireBox::encode_present(out);
264 } else {
265 WireBox::encode_absent(out);
266 }
267
268 Ok(())
269 }
270 }
271 };
272}
273
274macro_rules! impl_encode_for_primitives {
275 ($($ty:ty, $enc:ty);* $(;)?) => {
276 $(
277 impl_encode_for_primitive!($ty, $enc);
278 )*
279 }
280}
281
282impl_encode_for_primitives! {
283 (), (); bool, bool; i8, i8; u8, u8;
284
285 i16, WireI16; i32, WireI32; i64, WireI64;
286 u16, WireU16; u32, WireU32; u64, WireU64;
287 f32, WireF32; f64, WireF64;
288
289 WireI16, WireI16; WireI32, WireI32; WireI64, WireI64;
290 WireU16, WireU16; WireU32, WireU32; WireU64, WireU64;
291 WireF32, WireF32; WireF64, WireF64;
292}
293
294impl<T: Encodable, const N: usize> Encodable for [T; N] {
295 const COPY_OPTIMIZATION: CopyOptimization<Self> =
296 unsafe { CopyOptimization::enable_if(T::COPY_OPTIMIZATION.is_enabled()) };
297
298 type Encoded = [T::Encoded; N];
299}
300
301fn encode_to_array<A, E, T, const N: usize>(
302 value: A,
303 encoder: &mut E,
304 out: &mut MaybeUninit<[T::Encoded; N]>,
305) -> Result<(), EncodeError>
306where
307 A: AsRef<[T]> + IntoIterator,
308 A::Item: Encode<E, Encoded = T::Encoded>,
309 E: ?Sized,
310 T: Encode<E>,
311{
312 if T::COPY_OPTIMIZATION.is_enabled() {
313 unsafe {
315 copy_nonoverlapping(value.as_ref().as_ptr().cast(), out.as_mut_ptr(), 1);
316 }
317 } else {
318 for (i, item) in value.into_iter().enumerate() {
319 let out_i = unsafe { &mut *out.as_mut_ptr().cast::<MaybeUninit<T::Encoded>>().add(i) };
329 item.encode(encoder, out_i)?;
330 }
331 }
332 Ok(())
333}
334
335unsafe impl<E: ?Sized, T: Encode<E>, const N: usize> Encode<E> for [T; N] {
336 fn encode(
337 self,
338 encoder: &mut E,
339 out: &mut MaybeUninit<Self::Encoded>,
340 ) -> Result<(), EncodeError> {
341 encode_to_array(self, encoder, out)
342 }
343}
344
345unsafe impl<E: ?Sized, T: EncodeRef<E>, const N: usize> EncodeRef<E> for [T; N] {
346 fn encode_ref(
347 &self,
348 encoder: &mut E,
349 out: &mut MaybeUninit<Self::Encoded>,
350 ) -> Result<(), EncodeError> {
351 encode_to_array(self, encoder, out)
352 }
353}
354
355impl<T: Encodable> Encodable for Box<T> {
356 type Encoded = T::Encoded;
357}
358
359unsafe impl<E: ?Sized, T: Encode<E>> Encode<E> for Box<T> {
360 fn encode(
361 self,
362 encoder: &mut E,
363 out: &mut MaybeUninit<Self::Encoded>,
364 ) -> Result<(), EncodeError> {
365 T::encode(*self, encoder, out)
366 }
367}
368
369unsafe impl<E: ?Sized, T: EncodeRef<E>> EncodeRef<E> for Box<T> {
370 fn encode_ref(
371 &self,
372 encoder: &mut E,
373 out: &mut MaybeUninit<Self::Encoded>,
374 ) -> Result<(), EncodeError> {
375 T::encode_ref(self, encoder, out)
376 }
377}
378
379#[cfg(test)]
380mod tests {
381 use crate::chunks;
382 use crate::testing::assert_encoded;
383
384 #[test]
385 fn encode_unit() {
386 assert_encoded((), &chunks![]);
387 }
388
389 #[test]
390 fn encode_bool() {
391 assert_encoded(true, &chunks![0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
392 assert_encoded(false, &chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
393 }
394
395 #[test]
396 fn encode_ints() {
397 assert_encoded(0xa3u8, &chunks![0xa3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
398 assert_encoded(-0x45i8, &chunks![0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
399
400 assert_encoded(0x1234u16, &chunks![0x34, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
401 assert_encoded(-0x1234i16, &chunks![0xcc, 0xed, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
402
403 assert_encoded(0x12345678u32, &chunks![0x78, 0x56, 0x34, 0x12, 0x00, 0x00, 0x00, 0x00]);
404 assert_encoded(-0x12345678i32, &chunks![0x88, 0xa9, 0xcb, 0xed, 0x00, 0x00, 0x00, 0x00]);
405
406 assert_encoded(
407 0x123456789abcdef0u64,
408 &chunks![0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56, 0x34, 0x12],
409 );
410 assert_encoded(
411 -0x123456789abcdef0i64,
412 &chunks![0x10, 0x21, 0x43, 0x65, 0x87, 0xa9, 0xcb, 0xed],
413 );
414 }
415
416 #[test]
417 fn encode_floats() {
418 assert_encoded(
419 ::core::f32::consts::PI,
420 &chunks![0xdb, 0x0f, 0x49, 0x40, 0x00, 0x00, 0x00, 0x00],
421 );
422 assert_encoded(
423 ::core::f64::consts::PI,
424 &chunks![0x18, 0x2d, 0x44, 0x54, 0xfb, 0x21, 0x09, 0x40],
425 );
426 }
427
428 #[test]
429 fn encode_box() {
430 assert_encoded(None::<u64>, &chunks![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
431 assert_encoded(
432 Some(0x123456789abcdef0u64),
433 &chunks![
434 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0xde, 0xbc, 0x9a, 0x78, 0x56,
435 0x34, 0x12,
436 ],
437 );
438 }
439
440 #[test]
441 fn encode_vec() {
442 assert_encoded(
443 None::<Vec<u32>>,
444 &chunks![
445 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
446 0x00, 0x00,
447 ],
448 );
449 assert_encoded(
450 Some(vec![0x12345678u32, 0x9abcdef0u32]),
451 &chunks![
452 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
453 0xff, 0xff, 0x78, 0x56, 0x34, 0x12, 0xf0, 0xde, 0xbc, 0x9a,
454 ],
455 );
456 assert_encoded(
457 Some(Vec::<u32>::new()),
458 &chunks![
459 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
460 0xff, 0xff,
461 ],
462 );
463 }
464
465 #[test]
466 fn encode_string() {
467 assert_encoded(
468 None::<String>,
469 &chunks![
470 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
471 0x00, 0x00,
472 ],
473 );
474 assert_encoded(
475 Some("0123".to_string()),
476 &chunks![
477 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
478 0xff, 0xff, 0x30, 0x31, 0x32, 0x33, 0x00, 0x00, 0x00, 0x00,
479 ],
480 );
481 assert_encoded(
482 Some(String::new()),
483 &chunks![
484 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
485 0xff, 0xff,
486 ],
487 );
488 }
489}