Skip to content

Commit 1982814

Browse files
Add serde optional feature to serialize primitive types
1 parent 360bb2b commit 1982814

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.144", 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.107"
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)]
@@ -1530,3 +1560,82 @@ mod tests {
15301560
assert_eq!(format!("{:x?}", val), "U16(a)");
15311561
}
15321562
}
1563+
1564+
#[cfg(all(test, feature = "serde"))]
1565+
mod serialization_tests {
1566+
use core::fmt::Debug;
1567+
1568+
use serde::{Deserialize, Serialize};
1569+
1570+
use crate::{
1571+
byteorder::{Isize, Usize, F32, F64, I128, I16, I32, I64, U128, U16, U32, U64},
1572+
BigEndian, LittleEndian,
1573+
};
1574+
1575+
macro_rules! assert_serialization_roundtrip {
1576+
($prim:ty, $wrapper:ident, $value:expr) => {{
1577+
let primitive_value: $prim = $value;
1578+
assert_roundtrip(primitive_value, $wrapper::<BigEndian>::new(primitive_value));
1579+
assert_roundtrip(primitive_value, $wrapper::<LittleEndian>::new(primitive_value));
1580+
}};
1581+
}
1582+
1583+
fn assert_roundtrip<PrimitiveType, WrapperType>(
1584+
primitive_value: PrimitiveType,
1585+
wrapper_value: WrapperType,
1586+
) where
1587+
WrapperType: Serialize + for<'de> Deserialize<'de> + PartialEq + Debug,
1588+
PrimitiveType: Serialize + for<'de> Deserialize<'de> + PartialEq + Debug,
1589+
{
1590+
let serialized_value =
1591+
serde_json::to_string(&wrapper_value).expect("Serialization to json should succeed");
1592+
let deserialized_primitive = serde_json::from_str(&serialized_value)
1593+
.expect("Deserialization from json to primitive type should succeed");
1594+
let deserialized_wrapper = serde_json::from_str(&serialized_value)
1595+
.expect("Deserialization from json to wrapper type should succeed");
1596+
1597+
assert_eq!(primitive_value, deserialized_primitive);
1598+
assert_eq!(wrapper_value, deserialized_wrapper);
1599+
}
1600+
1601+
#[test]
1602+
fn serialize_native_primitives() {
1603+
assert_serialization_roundtrip!(u16, U16, 0xABCDu16);
1604+
assert_serialization_roundtrip!(i16, I16, -123i16);
1605+
assert_serialization_roundtrip!(u32, U32, 0x89AB_CDEFu32);
1606+
assert_serialization_roundtrip!(i32, I32, -0x1234_5678i32);
1607+
assert_serialization_roundtrip!(u64, U64, 0x0123_4567_89AB_CDEFu64);
1608+
assert_serialization_roundtrip!(i64, I64, -0x0123_4567_89AB_CDEFi64);
1609+
assert_serialization_roundtrip!(u128, U128, 0x1234u128);
1610+
assert_serialization_roundtrip!(i128, I128, -0x1234i128);
1611+
assert_serialization_roundtrip!(usize, Usize, 0xBEEFusize);
1612+
assert_serialization_roundtrip!(isize, Isize, -12isize);
1613+
assert_serialization_roundtrip!(f32, F32, 1.25f32);
1614+
assert_serialization_roundtrip!(f64, F64, -0.75f64);
1615+
}
1616+
1617+
#[derive(Serialize, Deserialize, PartialEq, Debug)]
1618+
struct SerializableStruct {
1619+
value_a: U16<BigEndian>,
1620+
value_b: [U16<LittleEndian>; 2],
1621+
}
1622+
1623+
#[test]
1624+
fn serialize_struct() {
1625+
let primitive_value_u16 = 0xABCDu16;
1626+
1627+
let primitive_value = SerializableStruct {
1628+
value_a: U16::<BigEndian>::new(primitive_value_u16),
1629+
value_b: [
1630+
U16::<LittleEndian>::new(primitive_value_u16),
1631+
U16::<LittleEndian>::new(primitive_value_u16),
1632+
],
1633+
};
1634+
let serialized_value =
1635+
serde_json::to_string(&primitive_value).expect("Serialization to json should succeed");
1636+
let deserialized_wrapper: SerializableStruct = serde_json::from_str(&serialized_value)
1637+
.expect("Deserialization from json should succeed");
1638+
1639+
assert_eq!(primitive_value, deserialized_wrapper);
1640+
}
1641+
}

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)