Skip to content

Commit 139f733

Browse files
Add serde optional feature to serialize primitive types
1 parent f65c938 commit 139f733

File tree

5 files changed

+131
-6
lines changed

5 files changed

+131
-6
lines changed

Cargo.toml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,9 +83,16 @@ std = ["alloc"]
8383
# This feature depends on all other features that work on the stable compiler.
8484
# We make no stability guarantees about this feature; it may be modified or
8585
# removed at any time.
86-
__internal_use_only_features_that_work_on_stable = ["alloc", "derive", "simd", "std"]
86+
__internal_use_only_features_that_work_on_stable = [
87+
"alloc",
88+
"derive",
89+
"serde",
90+
"simd",
91+
"std",
92+
]
8793

8894
[dependencies]
95+
serde = { version = "1.0.0", default-features = false, optional = true }
8996
zerocopy-derive = { version = "=0.8.29", path = "zerocopy-derive", optional = true }
9097

9198
# The "associated proc macro pattern" ensures that the versions of zerocopy and
@@ -106,6 +113,7 @@ rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
106113
rustversion = "1.0"
107114
static_assertions = "1.1"
108115
testutil = { path = "testutil" }
116+
serde_json = "1.0.0"
109117
# Pinned to a specific version so that the version used for local development
110118
# and the version used in CI are guaranteed to be the same. Future versions
111119
# sometimes change the output format slightly, so a version mismatch can cause

README.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ for network parsing.
121121
derives as `use zerocopy_derive::*` rather than by name (e.g., `use
122122
zerocopy_derive::FromBytes`).
123123

124+
- **`serde`**
125+
Provides `Serialize` and `Deserialize` impls for the `byteorder` numeric
126+
wrappers by delegating to their underlying primitive types.
127+
124128
- **`simd`**
125129
When the `simd` feature is enabled, `FromZeros`, `FromBytes`, and
126130
`IntoBytes` impls are emitted for all stable SIMD types which exist on the

src/byteorder.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -613,6 +613,36 @@ example of how it can be used for parsing UDP packets.
613613
}
614614
}
615615

616+
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
617+
#[cfg(feature = "serde")]
618+
impl<O: ByteOrder> serde::Serialize for $name<O>
619+
where
620+
$native: serde::Serialize,
621+
{
622+
#[inline(always)]
623+
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
624+
where
625+
S: serde::Serializer,
626+
{
627+
$native::serialize(&self.get(), serializer)
628+
}
629+
}
630+
631+
#[cfg_attr(doc_cfg, doc(cfg(feature = "serde")))]
632+
#[cfg(feature = "serde")]
633+
impl<'de, O: ByteOrder> serde::Deserialize<'de> for $name<O>
634+
where
635+
$native: serde::Deserialize<'de>,
636+
{
637+
#[inline(always)]
638+
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
639+
where
640+
D: serde::Deserializer<'de>,
641+
{
642+
$native::deserialize(deserializer).map(Self::from)
643+
}
644+
}
645+
616646
$(
617647
impl<O: ByteOrder> From<$name<O>> for $larger_native {
618648
#[inline(always)]
@@ -1521,6 +1551,85 @@ mod tests {
15211551
test!(@unary Neg, neg, call_for_signed_types, call_for_float_types);
15221552
}
15231553

1554+
#[cfg(feature = "serde")]
1555+
mod serde {
1556+
use core::fmt::Debug;
1557+
1558+
use serde::{Deserialize, Serialize};
1559+
1560+
use crate::{
1561+
byteorder::{Isize, Usize, F32, F64, I128, I16, I32, I64, U128, U16, U32, U64},
1562+
BigEndian, LittleEndian,
1563+
};
1564+
1565+
macro_rules! assert_serialization_roundtrip {
1566+
($prim:ty, $wrapper:ident, $value:expr) => {{
1567+
let primitive_value: $prim = $value;
1568+
assert_roundtrip(primitive_value, $wrapper::<BigEndian>::new(primitive_value));
1569+
assert_roundtrip(primitive_value, $wrapper::<LittleEndian>::new(primitive_value));
1570+
}};
1571+
}
1572+
1573+
fn assert_roundtrip<PrimitiveType, WrapperType>(
1574+
primitive_value: PrimitiveType,
1575+
wrapper_value: WrapperType,
1576+
) where
1577+
WrapperType: Serialize + for<'de> Deserialize<'de> + PartialEq + Debug,
1578+
PrimitiveType: Serialize + for<'de> Deserialize<'de> + PartialEq + Debug,
1579+
{
1580+
let serialized_value = serde_json::to_string(&wrapper_value)
1581+
.expect("Serialization to json should succeed");
1582+
let deserialized_primitive = serde_json::from_str(&serialized_value)
1583+
.expect("Deserialization from json to primitive type should succeed");
1584+
let deserialized_wrapper = serde_json::from_str(&serialized_value)
1585+
.expect("Deserialization from json to wrapper type should succeed");
1586+
1587+
assert_eq!(primitive_value, deserialized_primitive);
1588+
assert_eq!(wrapper_value, deserialized_wrapper);
1589+
}
1590+
1591+
#[test]
1592+
fn serialize_native_primitives() {
1593+
assert_serialization_roundtrip!(u16, U16, 0xABCDu16);
1594+
assert_serialization_roundtrip!(i16, I16, -123i16);
1595+
assert_serialization_roundtrip!(u32, U32, 0x89AB_CDEFu32);
1596+
assert_serialization_roundtrip!(i32, I32, -0x1234_5678i32);
1597+
assert_serialization_roundtrip!(u64, U64, 0x0123_4567_89AB_CDEFu64);
1598+
assert_serialization_roundtrip!(i64, I64, -0x0123_4567_89AB_CDEFi64);
1599+
assert_serialization_roundtrip!(u128, U128, 0x1234u128);
1600+
assert_serialization_roundtrip!(i128, I128, -0x1234i128);
1601+
assert_serialization_roundtrip!(usize, Usize, 0xBEEFusize);
1602+
assert_serialization_roundtrip!(isize, Isize, -12isize);
1603+
assert_serialization_roundtrip!(f32, F32, 1.25f32);
1604+
assert_serialization_roundtrip!(f64, F64, -0.75f64);
1605+
}
1606+
1607+
#[derive(Serialize, Deserialize, PartialEq, Debug)]
1608+
struct SerializableStruct {
1609+
value_a: U16<BigEndian>,
1610+
value_b: [U16<LittleEndian>; 2],
1611+
}
1612+
1613+
#[test]
1614+
fn serialize_struct() {
1615+
let primitive_value_u16 = 0xABCDu16;
1616+
1617+
let primitive_value = SerializableStruct {
1618+
value_a: U16::<BigEndian>::new(primitive_value_u16),
1619+
value_b: [
1620+
U16::<LittleEndian>::new(primitive_value_u16),
1621+
U16::<LittleEndian>::new(primitive_value_u16),
1622+
],
1623+
};
1624+
let serialized_value = serde_json::to_string(&primitive_value)
1625+
.expect("Serialization to json should succeed");
1626+
let deserialized_wrapper: SerializableStruct = serde_json::from_str(&serialized_value)
1627+
.expect("Deserialization from json should succeed");
1628+
1629+
assert_eq!(primitive_value, deserialized_wrapper);
1630+
}
1631+
}
1632+
15241633
#[test]
15251634
fn test_debug_impl() {
15261635
// Ensure that Debug applies format options to the inner value.

src/lib.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@
121121
//! derives as `use zerocopy_derive::*` rather than by name (e.g., `use
122122
//! zerocopy_derive::FromBytes`).
123123
//!
124+
//! - **`serde`**
125+
//! Provides `Serialize` and `Deserialize` impls for the `byteorder` numeric
126+
//! wrappers by delegating to their underlying primitive types.
127+
//!
124128
//! - **`simd`**
125129
//! When the `simd` feature is enabled, `FromZeros`, `FromBytes`, and
126130
//! `IntoBytes` impls are emitted for all stable SIMD types which exist on the

src/pointer/ptr.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1456,10 +1456,10 @@ mod tests {
14561456
}
14571457

14581458
test!(ZstDst, 8, 0, Some(0));
1459-
test!(ZstDst, 7, 0, None);
1459+
test!(ZstDst, 7, 0, None::<usize>);
14601460

14611461
test!(ZstDst, 8, usize::MAX, Some(usize::MAX));
1462-
test!(ZstDst, 7, usize::MAX, None);
1462+
test!(ZstDst, 7, usize::MAX, None::<usize>);
14631463

14641464
#[derive(KnownLayout, Immutable)]
14651465
#[repr(C)]
@@ -1469,14 +1469,14 @@ mod tests {
14691469
}
14701470

14711471
test!(Dst, 8, 0, Some(0));
1472-
test!(Dst, 7, 0, None);
1472+
test!(Dst, 7, 0, None::<usize>);
14731473

14741474
test!(Dst, 9, 1, Some(1));
1475-
test!(Dst, 8, 1, None);
1475+
test!(Dst, 8, 1, None::<usize>);
14761476

14771477
// If we didn't properly check for overflow, this would cause the
14781478
// metadata to overflow to 0, and thus the cast would spuriously
14791479
// succeed.
1480-
test!(Dst, 8, usize::MAX - 8 + 1, None);
1480+
test!(Dst, 8, usize::MAX - 8 + 1, None::<usize>);
14811481
}
14821482
}

0 commit comments

Comments
 (0)