Skip to content

Commit d9ed7f9

Browse files
Add serde optional feature to serialize primitive types
1 parent e781936 commit d9ed7f9

File tree

6 files changed

+178
-14
lines changed

6 files changed

+178
-14
lines changed

Cargo.toml

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,18 @@
1616
edition = "2021"
1717
name = "zerocopy"
1818
version = "0.8.31"
19-
authors = ["Joshua Liebow-Feeser <[email protected]>", "Jack Wrenn <[email protected]>"]
19+
authors = [
20+
"Joshua Liebow-Feeser <[email protected]>",
21+
"Jack Wrenn <[email protected]>",
22+
]
2023
description = "Zerocopy makes zero-cost memory manipulation effortless. We write \"unsafe\" so you don't have to."
21-
categories = ["embedded", "encoding", "no-std::no-alloc", "parsing", "rust-patterns"]
24+
categories = [
25+
"embedded",
26+
"encoding",
27+
"no-std::no-alloc",
28+
"parsing",
29+
"rust-patterns",
30+
]
2231
keywords = ["cast", "convert", "transmute", "transmutation", "type-punning"]
2332
license = "BSD-2-Clause OR Apache-2.0 OR MIT"
2433
repository = "https://github.com/google/zerocopy"
@@ -83,9 +92,16 @@ std = ["alloc"]
8392
# This feature depends on all other features that work on the stable compiler.
8493
# We make no stability guarantees about this feature; it may be modified or
8594
# removed at any time.
86-
__internal_use_only_features_that_work_on_stable = ["alloc", "derive", "simd", "std"]
95+
__internal_use_only_features_that_work_on_stable = [
96+
"alloc",
97+
"derive",
98+
"serde",
99+
"simd",
100+
"std",
101+
]
87102

88103
[dependencies]
104+
serde = { version = "1.0.144", default-features = false, optional = true }
89105
zerocopy-derive = { version = "=0.8.31", path = "zerocopy-derive", optional = true }
90106

91107
# The "associated proc macro pattern" ensures that the versions of zerocopy and
@@ -106,6 +122,7 @@ rand = { version = "0.8.5", default-features = false, features = ["small_rng"] }
106122
rustversion = "1.0"
107123
static_assertions = "1.1"
108124
testutil = { path = "testutil" }
125+
serde_json = "1.0.107"
109126
# Pinned to a specific version so that the version used for local development
110127
# and the version used in CI are guaranteed to be the same. Future versions
111128
# 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 [`serde`](https://github.com/serde-rs/serde) `Serialize` and `Deserialize` impls for
126+
the `byteorder` numeric 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: 131 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,104 @@ 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+
fn assert_roundtrip<PrimitiveType, WrapperType>(
1576+
to_serialize: WrapperType,
1577+
primitive_value: Option<PrimitiveType>,
1578+
) where
1579+
WrapperType: Serialize + for<'de> Deserialize<'de> + PartialEq + Debug,
1580+
PrimitiveType: Serialize + for<'de> Deserialize<'de> + PartialEq + Debug,
1581+
{
1582+
let serialized_value =
1583+
serde_json::to_string(&to_serialize).expect("Serialization to json should succeed");
1584+
let deserialized_wrapper = serde_json::from_str(&serialized_value)
1585+
.expect("Deserialization from json to wrapper type should succeed");
1586+
assert_eq!(to_serialize, deserialized_wrapper);
1587+
1588+
if let Some(primitive_value) = primitive_value {
1589+
let deserialized_primitive = serde_json::from_str(&serialized_value)
1590+
.expect("Deserialization from json to primitive type should succeed");
1591+
assert_eq!(primitive_value, deserialized_primitive);
1592+
}
1593+
}
1594+
1595+
macro_rules! assert_serialization_roundtrip {
1596+
($prim:ty, $wrapper:ident, $value:expr) => {{
1597+
let primitive_value: $prim = $value;
1598+
assert_roundtrip($wrapper::<BigEndian>::new(primitive_value), Some(primitive_value));
1599+
assert_roundtrip($wrapper::<LittleEndian>::new(primitive_value), Some(primitive_value));
1600+
}};
1601+
}
1602+
1603+
#[test]
1604+
fn serialize_native_primitives() {
1605+
assert_serialization_roundtrip!(u16, U16, 0xABCDu16);
1606+
assert_serialization_roundtrip!(i16, I16, -123i16);
1607+
assert_serialization_roundtrip!(u32, U32, 0x89AB_CDEFu32);
1608+
assert_serialization_roundtrip!(i32, I32, -0x1234_5678i32);
1609+
assert_serialization_roundtrip!(u64, U64, 0x0123_4567_89AB_CDEFu64);
1610+
assert_serialization_roundtrip!(i64, I64, -0x0123_4567_89AB_CDEFi64);
1611+
assert_serialization_roundtrip!(u128, U128, 0x1234u128);
1612+
assert_serialization_roundtrip!(i128, I128, -0x1234i128);
1613+
assert_serialization_roundtrip!(usize, Usize, 0xBEEFusize);
1614+
assert_serialization_roundtrip!(isize, Isize, -12isize);
1615+
assert_serialization_roundtrip!(f32, F32, 1.25f32);
1616+
assert_serialization_roundtrip!(f64, F64, -0.75f64);
1617+
}
1618+
1619+
#[derive(Serialize, Deserialize, PartialEq, Debug)]
1620+
struct SerializableStruct<WrapperTypeA, WrapperTypeB>
1621+
where
1622+
WrapperTypeA: PartialEq + Debug,
1623+
WrapperTypeB: PartialEq + Debug,
1624+
{
1625+
value_a: WrapperTypeA,
1626+
value_b: [WrapperTypeB; 2],
1627+
value_c: (WrapperTypeA, WrapperTypeB),
1628+
}
1629+
1630+
macro_rules! assert_struct_serialization_roundtrip {
1631+
($prim:ty, $wrapper:ident, $value:expr) => {{
1632+
let primitive_value: $prim = $value;
1633+
let test_struct = SerializableStruct {
1634+
value_a: $wrapper::<BigEndian>::new(primitive_value),
1635+
value_b: [
1636+
$wrapper::<LittleEndian>::new(primitive_value),
1637+
$wrapper::<LittleEndian>::new(primitive_value),
1638+
],
1639+
value_c: (
1640+
$wrapper::<BigEndian>::new(primitive_value),
1641+
$wrapper::<LittleEndian>::new(primitive_value),
1642+
),
1643+
};
1644+
assert_roundtrip(test_struct, None::<$prim>);
1645+
}};
1646+
}
1647+
1648+
#[test]
1649+
fn serialize_struct() {
1650+
assert_struct_serialization_roundtrip!(u16, U16, 0xABCDu16);
1651+
assert_struct_serialization_roundtrip!(i16, I16, -123i16);
1652+
assert_struct_serialization_roundtrip!(u32, U32, 0x89AB_CDEFu32);
1653+
assert_struct_serialization_roundtrip!(i32, I32, -0x1234_5678i32);
1654+
assert_struct_serialization_roundtrip!(u64, U64, 0x0123_4567_89AB_CDEFu64);
1655+
assert_struct_serialization_roundtrip!(i64, I64, -0x0123_4567_89AB_CDEFi64);
1656+
assert_struct_serialization_roundtrip!(u128, U128, 0x1234u128);
1657+
assert_struct_serialization_roundtrip!(i128, I128, -0x1234i128);
1658+
assert_struct_serialization_roundtrip!(usize, Usize, 0xBEEFusize);
1659+
assert_struct_serialization_roundtrip!(isize, Isize, -12isize);
1660+
assert_struct_serialization_roundtrip!(f32, F32, 1.25f32);
1661+
assert_struct_serialization_roundtrip!(f64, F64, -0.75f64);
1662+
}
1663+
}

src/lib.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -121,9 +121,13 @@
121121
//! derives as `use zerocopy_derive::*` rather than by name (e.g., `use
122122
//! zerocopy_derive::FromBytes`).
123123
//!
124+
//! - **`serde`**
125+
//! Provides [`serde`](https://github.com/serde-rs/serde) `Serialize` and `Deserialize` impls for
126+
//! the `byteorder` numeric wrappers by delegating to their underlying primitive types.
127+
//!
124128
//! - **`simd`**
125-
//! When the `simd` feature is enabled, `FromZeros`, `FromBytes`, and
126-
//! `IntoBytes` impls are emitted for all stable SIMD types which exist on the
129+
//! When the `simd` feature is enabled, [`FromZeros`], [`FromBytes`], and
130+
//! [`IntoBytes`] impls are emitted for all stable SIMD types which exist on the
127131
//! target platform. Note that the layout of SIMD types is not yet stabilized,
128132
//! so these impls may be removed in the future if layout changes make them
129133
//! invalid. For more information, see the Unsafe Code Guidelines Reference

src/pointer/ptr.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1465,10 +1465,10 @@ mod tests {
14651465
}
14661466

14671467
test!(ZstDst, 8, 0, Some(0));
1468-
test!(ZstDst, 7, 0, None);
1468+
test!(ZstDst, 7, 0, None::<usize>);
14691469

14701470
test!(ZstDst, 8, usize::MAX, Some(usize::MAX));
1471-
test!(ZstDst, 7, usize::MAX, None);
1471+
test!(ZstDst, 7, usize::MAX, None::<usize>);
14721472

14731473
#[derive(KnownLayout, Immutable)]
14741474
#[repr(C)]
@@ -1478,14 +1478,14 @@ mod tests {
14781478
}
14791479

14801480
test!(Dst, 8, 0, Some(0));
1481-
test!(Dst, 7, 0, None);
1481+
test!(Dst, 7, 0, None::<usize>);
14821482

14831483
test!(Dst, 9, 1, Some(1));
1484-
test!(Dst, 8, 1, None);
1484+
test!(Dst, 8, 1, None::<usize>);
14851485

14861486
// If we didn't properly check for overflow, this would cause the
14871487
// metadata to overflow to 0, and thus the cast would spuriously
14881488
// succeed.
1489-
test!(Dst, 8, usize::MAX - 8 + 1, None);
1489+
test!(Dst, 8, usize::MAX - 8 + 1, None::<usize>);
14901490
}
14911491
}

tools/generate-readme/src/main.rs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -37,15 +37,23 @@ fn main() {
3737

3838
let readme = String::from_utf8(output.stdout).unwrap();
3939

40-
// This regex is used to strip code links like:
40+
// This regex is used to strip code links without url after, like:
4141
//
4242
// /// Here is a link to [`Vec`].
4343
//
4444
// These links don't work in a Markdown file, and so we remove the `[` and `]`
4545
// characters to convert them to non-link code snippets.
46-
let body = Regex::new(r"\[(`[^`]*`)\]")
47-
.unwrap()
48-
.replace_all(&readme, |caps: &Captures| caps[1].to_string());
46+
let re = Regex::new(r"\[(`[^`]*`)\](\([^)]*\))?").unwrap();
47+
48+
let body = re.replace_all(&readme, |caps: &Captures| {
49+
if caps.get(2).is_some() {
50+
// There is a following `(...)`: keep the whole original text
51+
caps[0].to_string()
52+
} else {
53+
// No `(...)`: strip the surrounding [ ]
54+
caps[1].to_string()
55+
}
56+
});
4957

5058
println!("{}\n\n{}\n{}", COPYRIGHT_HEADER, body, DISCLAIMER_FOOTER);
5159
}

0 commit comments

Comments
 (0)