1use core::marker::PhantomData;
44
45use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Visitor};
46
47#[cfg(feature = "alloc")]
48use alloc::string::ToString;
49
50impl<const SCALE: u32> Serialize for crate::D<crate::int::types::Int<2>, SCALE> {
53 #[inline]
60 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
61 if serializer.is_human_readable() {
62 #[cfg(feature = "alloc")]
66 {
67 serializer.serialize_str(&self.0.to_string())
68 }
69 #[cfg(not(feature = "alloc"))]
74 {
75 let _ = serializer;
76 Err(serde::ser::Error::custom(
77 "decimal-scaled: human-readable serialisation requires the `alloc` feature",
78 ))
79 }
80 } else {
81 serializer.serialize_bytes(&self.0.as_i128().to_le_bytes())
83 }
84 }
85}
86
87impl<'de, const SCALE: u32> Deserialize<'de> for crate::D<crate::int::types::Int<2>, SCALE> {
90 #[inline]
103 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
104 let visitor = decimal_serde::DecimalVisitor::<SCALE>(PhantomData);
105 if deserializer.is_human_readable() {
106 deserializer.deserialize_any(visitor)
107 } else {
108 deserializer.deserialize_bytes(visitor)
109 }
110 }
111}
112
113pub mod decimal_serde {
135 use super::{Deserialize, Deserializer, PhantomData, Serialize, Serializer, Visitor};
136
137 #[inline]
146 pub fn serialize<const SCALE: u32, S: Serializer>(
147 value: &crate::D<crate::int::types::Int<2>, SCALE>,
148 serializer: S,
149 ) -> Result<S::Ok, S::Error> {
150 value.serialize(serializer)
151 }
152
153 #[inline]
162 pub fn deserialize<'de, const SCALE: u32, D: Deserializer<'de>>(
163 deserializer: D,
164 ) -> Result<crate::D<crate::int::types::Int<2>, SCALE>, D::Error> {
165 crate::D::<crate::int::types::Int<2>, SCALE>::deserialize(deserializer)
166 }
167
168 pub struct DecimalVisitor<const SCALE: u32>(pub PhantomData<()>);
182
183 impl<'de, const SCALE: u32> Visitor<'de> for DecimalVisitor<SCALE> {
184 type Value = crate::D<crate::int::types::Int<2>, SCALE>;
185
186 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
187 formatter.write_str(
188 "a base-10 i128 integer string, 16 little-endian bytes, \
189 or a native integer",
190 )
191 }
192
193 fn visit_str<E: serde::de::Error>(self, text: &str) -> Result<Self::Value, E> {
196 let bytes = text.as_bytes();
206 if bytes.is_empty() {
207 return Err(serde::de::Error::custom(
208 "decimal-scaled: empty string is not a valid i128 wire",
209 ));
210 }
211 if bytes[0] == b'+' {
212 return Err(serde::de::Error::custom(
213 "decimal-scaled: leading `+` is not part of the canonical wire format",
214 ));
215 }
216 text.parse::<i128>().map(|parsed| crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(parsed))).map_err(|_| {
217 serde::de::Error::custom("decimal-scaled: expected a base-10 i128 integer string")
218 })
219 }
220
221 fn visit_borrowed_str<E: serde::de::Error>(self, text: &'de str) -> Result<Self::Value, E> {
222 self.visit_str(text)
223 }
224
225 #[cfg(feature = "alloc")]
226 fn visit_string<E: serde::de::Error>(
227 self,
228 text: alloc::string::String,
229 ) -> Result<Self::Value, E> {
230 self.visit_str(&text)
231 }
232
233 fn visit_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
236 let le_bytes: [u8; 16] = bytes.try_into().map_err(|_| {
238 serde::de::Error::invalid_length(
239 bytes.len(),
240 &"exactly 16 little-endian bytes for an i128",
241 )
242 })?;
243 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from_le_bytes(le_bytes))))
244 }
245
246 fn visit_borrowed_bytes<E: serde::de::Error>(self, bytes: &'de [u8]) -> Result<Self::Value, E> {
247 self.visit_bytes(bytes)
248 }
249
250 #[cfg(feature = "alloc")]
251 fn visit_byte_buf<E: serde::de::Error>(
252 self,
253 bytes: alloc::vec::Vec<u8>,
254 ) -> Result<Self::Value, E> {
255 self.visit_bytes(&bytes)
256 }
257
258 fn visit_i8<E: serde::de::Error>(self, value: i8) -> Result<Self::Value, E> {
267 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(value))))
268 }
269
270 fn visit_i16<E: serde::de::Error>(self, value: i16) -> Result<Self::Value, E> {
271 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(value))))
272 }
273
274 fn visit_i32<E: serde::de::Error>(self, value: i32) -> Result<Self::Value, E> {
275 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(value))))
276 }
277
278 fn visit_i64<E: serde::de::Error>(self, value: i64) -> Result<Self::Value, E> {
279 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(value))))
280 }
281
282 fn visit_i128<E: serde::de::Error>(self, value: i128) -> Result<Self::Value, E> {
283 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(value)))
284 }
285
286 fn visit_u8<E: serde::de::Error>(self, value: u8) -> Result<Self::Value, E> {
287 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(value))))
288 }
289
290 fn visit_u16<E: serde::de::Error>(self, value: u16) -> Result<Self::Value, E> {
291 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(value))))
292 }
293
294 fn visit_u32<E: serde::de::Error>(self, value: u32) -> Result<Self::Value, E> {
295 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(value))))
296 }
297
298 fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<Self::Value, E> {
299 Ok(crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(i128::from(value))))
300 }
301
302 fn visit_u128<E: serde::de::Error>(self, value: u128) -> Result<Self::Value, E> {
303 i128::try_from(value).map(|narrowed| crate::D::<crate::int::types::Int<2>, SCALE>::from_bits(crate::int::types::Int::<2>::from_i128(narrowed))).map_err(|_| {
306 serde::de::Error::custom("decimal-scaled: u128 value exceeds i128 storage range")
307 })
308 }
309
310 }
319}
320
321#[cfg(all(test, feature = "alloc", feature = "serde"))]
324mod tests {
325 use super::*;
326 use crate::types::widths::D38s12;
327 use alloc::format;
328 use serde::de::IntoDeserializer;
329 use serde::de::value::{Error as DeError, StrDeserializer};
330
331 #[test]
335 fn deserialize_canonical_zero_string() {
336 let deserializer: StrDeserializer<DeError> = "0".into_deserializer();
337 let value: D38s12 = D38s12::deserialize(deserializer).unwrap();
338 assert_eq!(value, D38s12::ZERO);
339 }
340
341 #[test]
344 fn visitor_accepts_scaled_one_str() {
345 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
346 let value: D38s12 = <_ as Visitor>::visit_str::<DeError>(visitor, "1000000000000").unwrap();
347 assert_eq!(value, D38s12::ONE);
348 }
349
350 #[test]
353 fn visitor_rejects_decimal_point_str() {
354 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
355 let parsed: Result<D38s12, _> = <_ as Visitor>::visit_str::<DeError>(visitor, "1.5");
356 assert!(parsed.is_err(), "expected reject; got Ok({:?})", parsed);
357 }
358
359 #[test]
364 fn visitor_accepts_i64_as_storage() {
365 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
366 let value: D38s12 = <_ as Visitor>::visit_i64::<DeError>(visitor, -5).unwrap();
367 assert_eq!(value.to_bits(), -5);
368 }
369
370 #[test]
372 fn visitor_accepts_u64_max() {
373 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
374 let value: D38s12 = <_ as Visitor>::visit_u64::<DeError>(visitor, u64::MAX).unwrap();
375 assert_eq!(value.to_bits(), u64::MAX as i128);
376 }
377
378 #[test]
381 fn visitor_rejects_u128_above_i128_max() {
382 let visitor = decimal_serde::DecimalVisitor::<12>(PhantomData);
383 let parsed: Result<D38s12, _> =
384 <_ as Visitor>::visit_u128::<DeError>(visitor, (i128::MAX as u128) + 1);
385 assert!(
386 parsed.is_err(),
387 "expected overflow reject; got Ok({:?})",
388 parsed
389 );
390 }
391
392 #[test]
398 fn json_one_serialises_as_scaled_integer_string() {
399 let json = serde_json::to_string(&D38s12::ONE).unwrap();
400 assert_eq!(json, "\"1000000000000\"");
401 }
402
403 #[test]
404 fn json_zero_serialises_as_zero_string() {
405 let json = serde_json::to_string(&D38s12::ZERO).unwrap();
406 assert_eq!(json, "\"0\"");
407 }
408
409 #[test]
410 fn json_one_round_trips() {
411 let json = serde_json::to_string(&D38s12::ONE).unwrap();
412 let back: D38s12 = serde_json::from_str(&json).unwrap();
413 assert_eq!(back, D38s12::ONE);
414 }
415
416 #[test]
417 fn json_zero_round_trips() {
418 let json = serde_json::to_string(&D38s12::ZERO).unwrap();
419 let back: D38s12 = serde_json::from_str(&json).unwrap();
420 assert_eq!(back, D38s12::ZERO);
421 }
422
423 #[test]
426 fn json_negative_round_trips() {
427 let value = D38s12::try_from(-5_i32).unwrap();
428 let json = serde_json::to_string(&value).unwrap();
429 assert_eq!(json, "\"-5000000000000\"");
430 let back: D38s12 = serde_json::from_str(&json).unwrap();
431 assert_eq!(back, value);
432 assert_eq!(back.to_bits(), -5_000_000_000_000_i128);
433 }
434
435 #[test]
438 fn json_max_round_trips() {
439 let json = serde_json::to_string(&D38s12::MAX).unwrap();
440 let back: D38s12 = serde_json::from_str(&json).unwrap();
441 assert_eq!(back, D38s12::MAX);
442 }
443
444 #[test]
445 fn json_min_round_trips() {
446 let json = serde_json::to_string(&D38s12::MIN).unwrap();
447 let back: D38s12 = serde_json::from_str(&json).unwrap();
448 assert_eq!(back, D38s12::MIN);
449 }
450
451 #[test]
455 fn json_string_matches_i128_to_string() {
456 let raw: i128 = -123_456_789_012_345_678_901_234_567_890_i128;
457 let value = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(raw));
458 let json = serde_json::to_string(&value).unwrap();
459 assert_eq!(json, format!("\"{}\"", raw));
460 }
461
462 #[test]
465 fn json_rejects_decimal_point_string() {
466 let parsed: Result<D38s12, _> = serde_json::from_str("\"1.5\"");
467 assert!(parsed.is_err(), "expected reject; got Ok({:?})", parsed);
468 }
469
470 #[test]
471 fn json_rejects_scientific_notation_string() {
472 let parsed: Result<D38s12, _> = serde_json::from_str("\"1e6\"");
473 assert!(parsed.is_err(), "expected reject; got Ok({:?})", parsed);
474 }
475
476 #[test]
477 fn json_rejects_not_a_number_string() {
478 let parsed: Result<D38s12, _> = serde_json::from_str("\"not-a-number\"");
479 assert!(parsed.is_err(), "expected reject; got Ok({:?})", parsed);
480 }
481
482 #[test]
483 fn json_rejects_empty_string() {
484 let parsed: Result<D38s12, _> = serde_json::from_str("\"\"");
485 assert!(parsed.is_err(), "expected reject; got Ok({:?})", parsed);
486 }
487
488 #[test]
489 fn json_rejects_leading_whitespace_string() {
490 let parsed: Result<D38s12, _> = serde_json::from_str("\" 42\"");
493 assert!(parsed.is_err(), "expected reject; got Ok({:?})", parsed);
494 }
495
496 #[test]
497 fn json_rejects_plus_prefix() {
498 let parsed: Result<D38s12, _> = serde_json::from_str("\"+42\"");
499 assert!(parsed.is_err(), "expected reject; got Ok({:?})", parsed);
500 }
501
502 #[test]
505 fn json_accepts_bare_integer_number_as_storage() {
506 let back: D38s12 = serde_json::from_str("42").unwrap();
507 assert_eq!(back.to_bits(), 42_i128);
508 }
509
510 #[test]
513 fn postcard_one_round_trips() {
514 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::ONE).unwrap();
515 let raw = D38s12::ONE.to_bits().as_i128().to_le_bytes();
518 assert!(bytes.windows(16).any(|window| window == raw));
519 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
520 assert_eq!(back, D38s12::ONE);
521 }
522
523 #[test]
524 fn postcard_zero_round_trips() {
525 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::ZERO).unwrap();
526 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
527 assert_eq!(back, D38s12::ZERO);
528 }
529
530 #[test]
531 fn postcard_negative_round_trips() {
532 let value = D38s12::try_from(-5_i32).unwrap();
533 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&value).unwrap();
534 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
535 assert_eq!(back, value);
536 }
537
538 #[test]
539 fn postcard_max_round_trips() {
540 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::MAX).unwrap();
541 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
542 assert_eq!(back, D38s12::MAX);
543 }
544
545 #[test]
546 fn postcard_min_round_trips() {
547 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&D38s12::MIN).unwrap();
548 let back: D38s12 = postcard::from_bytes(&bytes).unwrap();
549 assert_eq!(back, D38s12::MIN);
550 }
551
552 #[test]
556 fn postcard_byte_order_matches_le() {
557 let value = D38s12::from_bits(crate::int::types::Int::<2>::from_i128(0x0123_4567_89AB_CDEF_FEDC_BA98_7654_3210_i128));
558 let bytes: alloc::vec::Vec<u8> = postcard::to_allocvec(&value).unwrap();
559 let raw = value.to_bits().as_i128().to_le_bytes();
560 let found = bytes.windows(16).position(|window| window == raw);
561 assert!(
562 found.is_some(),
563 "expected raw LE bytes embedded; got {:?}",
564 bytes
565 );
566 assert_eq!(raw[0], 0x10); assert_eq!(raw[15], 0x01); }
569
570 #[test]
575 fn cross_format_json_string_matches_le_bytes() {
576 let value = D38s12::try_from(42_i32).unwrap();
577 let json = serde_json::to_string(&value).unwrap();
578 let inner = json.trim_matches('"');
579 let parsed: i128 = inner.parse().unwrap();
580 let json_bytes = parsed.to_le_bytes();
581 let direct_bytes = value.to_bits().as_i128().to_le_bytes();
582 assert_eq!(json_bytes, direct_bytes);
583 }
584
585 #[test]
589 fn cross_scale_wire_is_storage_only() {
590 let raw: i128 = 1_500_000_000_000;
591 let at_scale_12 = crate::D::<crate::int::types::Int<2>, 12>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
592 let at_scale_6 = crate::D::<crate::int::types::Int<2>, 6>::from_bits(crate::int::types::Int::<2>::from_i128(raw));
593 assert_eq!(serde_json::to_string(&at_scale_12).unwrap(), "\"1500000000000\"");
594 assert_eq!(serde_json::to_string(&at_scale_6).unwrap(), "\"1500000000000\"");
595 }
596
597 #[test]
602 fn decimal_serde_helper_round_trips() {
603 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq)]
604 struct Holder {
605 #[serde(with = "crate::serde_helpers::decimal_serde")]
606 length: crate::D<crate::int::types::Int<2>, 12>,
607 }
608
609 let holder = Holder {
610 length: D38s12::try_from(7_i32).unwrap(),
611 };
612 let json = serde_json::to_string(&holder).unwrap();
613 assert_eq!(json, r#"{"length":"7000000000000"}"#);
614 let back: Holder = serde_json::from_str(&json).unwrap();
615 assert_eq!(back, holder);
616 }
617}
618
619#[cfg(feature = "_wide-support")]
632macro_rules! decl_wide_serde {
633 ($Type:ident, $Storage:ty, $bytes_len:literal) => {
634 impl<const SCALE: u32> Serialize for $crate::types::widths::$Type<SCALE> {
635 #[inline]
639 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
640 if serializer.is_human_readable() {
641 #[cfg(feature = "alloc")]
642 {
643 serializer.serialize_str(&self.0.to_string())
644 }
645 #[cfg(not(feature = "alloc"))]
646 {
647 let _ = serializer;
648 Err(serde::ser::Error::custom(
649 "decimal-scaled: human-readable serialisation requires `alloc`",
650 ))
651 }
652 } else {
653 let mut bytes = [0u8; $bytes_len];
654 let limbs = self.0.limbs_le();
655 for (i, limb) in limbs.iter().enumerate() {
660 bytes[i * 8..(i + 1) * 8].copy_from_slice(&limb.to_le_bytes());
661 }
662 serializer.serialize_bytes(&bytes)
663 }
664 }
665 }
666
667 impl<'de, const SCALE: u32> Deserialize<'de> for $crate::types::widths::$Type<SCALE> {
668 #[inline]
669 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
670 struct V<const S: u32>;
671 impl<'de, const S: u32> Visitor<'de> for V<S> {
672 type Value = $crate::types::widths::$Type<S>;
673 fn expecting(&self, formatter: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
674 formatter.write_str(concat!(
675 "a base-10 integer string or ",
676 stringify!($bytes_len),
677 " little-endian bytes for ",
678 stringify!($Type),
679 ))
680 }
681 fn visit_str<E: serde::de::Error>(self, text: &str) -> Result<Self::Value, E> {
682 let parsed = <$Storage>::from_str_radix(text, 10).map_err(|_| {
683 serde::de::Error::custom(concat!(
684 stringify!($Type),
685 ": invalid base-10 integer string",
686 ))
687 })?;
688 Ok(<$crate::types::widths::$Type<S>>::from_bits(parsed))
689 }
690 fn visit_borrowed_str<E: serde::de::Error>(
691 self,
692 text: &'de str,
693 ) -> Result<Self::Value, E> {
694 self.visit_str(text)
695 }
696 #[cfg(feature = "alloc")]
697 fn visit_string<E: serde::de::Error>(
698 self,
699 text: alloc::string::String,
700 ) -> Result<Self::Value, E> {
701 self.visit_str(&text)
702 }
703 fn visit_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
704 if bytes.len() != $bytes_len {
705 return Err(serde::de::Error::invalid_length($bytes_len, &self));
706 }
707 let mut limbs = [0u64; $bytes_len / 8];
709 for (i, limb) in limbs.iter_mut().enumerate() {
710 let mut buf = [0u8; 8];
711 buf.copy_from_slice(&bytes[i * 8..(i + 1) * 8]);
712 *limb = u64::from_le_bytes(buf);
713 }
714 Ok(<$crate::types::widths::$Type<S>>::from_bits(
715 <$Storage>::from_limbs_le(limbs),
716 ))
717 }
718 fn visit_borrowed_bytes<E: serde::de::Error>(
719 self,
720 bytes: &'de [u8],
721 ) -> Result<Self::Value, E> {
722 self.visit_bytes(bytes)
723 }
724 }
725 if deserializer.is_human_readable() {
726 deserializer.deserialize_str(V::<SCALE>)
727 } else {
728 deserializer.deserialize_bytes(V::<SCALE>)
729 }
730 }
731 }
732 };
733}
734
735#[cfg(any(feature = "d57", feature = "wide"))]
736decl_wide_serde!(D57, crate::int::types::Int<3>, 24);
737#[cfg(any(feature = "d76", feature = "wide"))]
738decl_wide_serde!(D76, crate::int::types::Int<4>, 32);
739#[cfg(any(feature = "d115", feature = "wide"))]
740decl_wide_serde!(D115, crate::int::types::Int<6>, 48);
741#[cfg(any(feature = "d153", feature = "wide"))]
742decl_wide_serde!(D153, crate::int::types::Int<8>, 64);
743#[cfg(any(feature = "d230", feature = "wide"))]
744decl_wide_serde!(D230, crate::int::types::Int<12>, 96);
745#[cfg(any(feature = "d307", feature = "wide", feature = "x-wide"))]
746decl_wide_serde!(D307, crate::int::types::Int<16>, 128);
747#[cfg(any(feature = "d462", feature = "x-wide"))]
748decl_wide_serde!(D462, crate::int::types::Int<24>, 192);
749#[cfg(any(feature = "d616", feature = "x-wide"))]
750decl_wide_serde!(D616, crate::int::types::Int<32>, 256);
751#[cfg(any(feature = "d924", feature = "xx-wide"))]
752decl_wide_serde!(D924, crate::int::types::Int<48>, 384);
753#[cfg(any(feature = "d1232", feature = "xx-wide"))]
754decl_wide_serde!(D1232, crate::int::types::Int<64>, 512);
755
756#[cfg(all(test, feature = "wide"))]
757mod wide_serde_tests {
758
759 #[test]
760 fn d76_human_readable_round_trip() {
761 let value = crate::D::<crate::int::types::Int<4>, 12>::try_from(1_234_567_i128).unwrap();
762 let json = serde_json::to_string(&value).unwrap();
763 let back: crate::D<crate::int::types::Int<4>, 12> = serde_json::from_str(&json).unwrap();
764 assert_eq!(back, value);
765 }
766
767 #[test]
768 fn d76_negative_human_readable_round_trip() {
769 let value = -crate::D::<crate::int::types::Int<4>, 12>::try_from(987_654_321_i128).unwrap();
770 let json = serde_json::to_string(&value).unwrap();
771 let back: crate::D<crate::int::types::Int<4>, 12> = serde_json::from_str(&json).unwrap();
772 assert_eq!(back, value);
773 }
774
775 #[test]
776 fn d76_binary_round_trip() {
777 let value = crate::D::<crate::int::types::Int<4>, 12>::try_from(42_i128).unwrap();
779 let bytes = postcard::to_allocvec(&value).unwrap();
780 let back: crate::D<crate::int::types::Int<4>, 12> = postcard::from_bytes(&bytes).unwrap();
781 assert_eq!(back, value);
782 }
783}