@@ -57,3 +57,112 @@ macro_rules! test_roundtrip {
5757 } ;
5858 } } ;
5959}
60+
61+ #[ macro_export]
62+ macro_rules! test_datatype_roundtrip {
63+ // ---- special rule for bool ----
64+ // Bool has a non-canonical encoding in the spec: only the lowest bit is meaningful.
65+ // Multiple byte values can parse to the same logical bool, so we cannot require a
66+ // strict byte-for-byte roundtrip. Instead we check semantic stability and canonicalization.
67+ ( bool , $data: expr) => { {
68+ let mut input = $data. clone( ) ;
69+
70+ // Only run the roundtrip checks if parsing succeeds. Invalid inputs are ignored,
71+ // because this macro validates stability of valid encodings, not rejection behavior.
72+ if let Ok ( parsed) = bool :: from_bytes( & mut input) {
73+ // Allocate exactly the number of bytes required by the parsed value.
74+ // This ensures we test the canonical serialized size.
75+ let mut encoded = vec![ 0u8 ; parsed. get_size( ) ] ;
76+
77+ // A successful parse must always be serializable.
78+ parsed
79+ . to_bytes( & mut encoded)
80+ . expect( "Bool encoding failed after a successfull parse" ) ;
81+
82+ // Bytes produced by serialization must always be parseable again.
83+ let reparsed = bool :: from_bytes( & mut encoded)
84+ . expect( "The bytes generated from a valid bool should be parseable" ) ;
85+
86+ // Logical value must be preserved by parse → serialize → parse.
87+ assert_eq!( parsed, reparsed, "Bool roundtrip is not stable" ) ;
88+
89+ // Because only the lowest bit is significant, we compare the semantic bit,
90+ // not the full original byte. This verifies canonical encoding.
91+ assert_eq!( input[ 0 ] & 1 , encoded[ 0 ] , "Bool serialization is not stable" ) ;
92+ }
93+ } } ;
94+
95+ // ---- special rule for f32 ----
96+ // Floats require bit-level comparison IEEE-754.
97+ ( f32 , $data: expr) => { {
98+ let mut input = $data. clone( ) ;
99+
100+ // Only validate successful parses; invalid encodings are outside this macro’s scope.
101+ if let Ok ( parsed) = f32 :: from_bytes( & mut input) {
102+ // Allocate the exact canonical size of the float representation.
103+ let mut encoded = vec![ 0u8 ; parsed. get_size( ) ] ;
104+
105+ // A successfully parsed float must serialize without failure.
106+ parsed
107+ . to_bytes( & mut encoded)
108+ . expect( "Encoding failed after a successful parse" ) ;
109+
110+ // Serialized bytes must be parseable back into a float.
111+ let reparsed = f32 :: from_bytes( & mut encoded)
112+ . expect( "The bytes generated from a valid datatype should be parseable" ) ;
113+
114+ // Compare raw bits to enforce strict roundtrip stability, including NaN payloads.
115+ assert_eq!(
116+ parsed. to_bits( ) ,
117+ reparsed. to_bits( ) ,
118+ "Float roundtrip is not bit-stable"
119+ ) ;
120+
121+ // Ensure serialization is canonical: re-encoding must match the consumed input.
122+ assert_eq!(
123+ encoded,
124+ input[ ..encoded. get_size( ) ] ,
125+ "Serialization is not stable"
126+ ) ;
127+ }
128+ } } ;
129+
130+ // ---- generic rule ----
131+ ( $datatype: ty, $data: expr) => { {
132+ let mut input = $data. clone( ) ;
133+
134+ // Only test successful parses; this macro checks roundtrip invariants.
135+ if let Ok ( parsed) = <$datatype>:: from_bytes( & mut input) {
136+ // Allocate exactly the canonical serialized size.
137+ let mut encoded = vec![ 0u8 ; parsed. get_size( ) ] ;
138+
139+ // A parsed value must always serialize successfully.
140+ parsed. clone( ) . to_bytes( & mut encoded) . expect( concat!(
141+ stringify!( $datatype) ,
142+ ": Encoding failed after a successful parse"
143+ ) ) ;
144+
145+ // Serialized bytes must be parseable again into the same datatype.
146+ let reparsed = <$datatype>:: from_bytes( & mut encoded) . expect( concat!(
147+ stringify!( $datatype) ,
148+ ": The bytes generated from a valid datatype should be parseable"
149+ ) ) ;
150+
151+ // Semantic equality after roundtrip is required.
152+ assert_eq!(
153+ parsed,
154+ reparsed,
155+ "{}: The roundtrip should produce the same message" ,
156+ stringify!( $datatype)
157+ ) ;
158+
159+ // reserialization must match the consumed input bytes.
160+ assert_eq!(
161+ encoded,
162+ input[ ..encoded. get_size( ) ] ,
163+ "{}: Serialization is not stable" ,
164+ stringify!( $datatype)
165+ ) ;
166+ }
167+ } } ;
168+ }
0 commit comments