@@ -931,6 +931,70 @@ macro_rules! impl_copy_codec {
931931 } ;
932932}
933933
934+ // The Component Model canonical ABI mandates a single canonical `NaN`
935+ // representation for floating point values. Encoding canonicalizes `NaN`s to
936+ // match; decoding is lenient and accepts any `NaN` representation.
937+ //
938+ // See `canonicalize_nan{32,64}` in
939+ // <https://github.com/WebAssembly/component-model/blob/main/design/mvp/canonical-abi/definitions.py>.
940+ const CANONICAL_NAN_F32 : u32 = 0x7fc0_0000 ;
941+ const CANONICAL_NAN_F64 : u64 = 0x7ff8_0000_0000_0000 ;
942+
943+ /// Defines a floating-point codec that canonicalizes `NaN` values on encode to
944+ /// match the Component Model canonical ABI, delegating the actual byte encoding
945+ /// and decoding to the wrapped `wasm-tokio` codec.
946+ macro_rules! impl_canonical_nan_codec {
947+ ( $name: ident, $inner: ty, $t: ty, $canon: expr) => {
948+ #[ doc = concat!( "Canonicalizes `NaN`s on encode, wrapping [`" , stringify!( $inner) , "`]." ) ]
949+ #[ derive( Debug , Default ) ]
950+ pub struct $name( $inner) ;
951+
952+ impl tokio_util:: codec:: Encoder <$t> for $name {
953+ type Error = std:: io:: Error ;
954+
955+ fn encode( & mut self , item: $t, dst: & mut BytesMut ) -> Result <( ) , Self :: Error > {
956+ let item = if item. is_nan( ) {
957+ <$t>:: from_bits( $canon)
958+ } else {
959+ item
960+ } ;
961+ self . 0 . encode( item, dst)
962+ }
963+ }
964+
965+ impl tokio_util:: codec:: Encoder <& $t> for $name {
966+ type Error = std:: io:: Error ;
967+
968+ fn encode( & mut self , item: & $t, dst: & mut BytesMut ) -> Result <( ) , Self :: Error > {
969+ tokio_util:: codec:: Encoder :: <$t>:: encode( self , * item, dst)
970+ }
971+ }
972+
973+ impl tokio_util:: codec:: Encoder <&&$t> for $name {
974+ type Error = std:: io:: Error ;
975+
976+ fn encode( & mut self , item: &&$t, dst: & mut BytesMut ) -> Result <( ) , Self :: Error > {
977+ tokio_util:: codec:: Encoder :: <$t>:: encode( self , * * item, dst)
978+ }
979+ }
980+
981+ impl tokio_util:: codec:: Decoder for $name {
982+ type Item = $t;
983+ type Error = std:: io:: Error ;
984+
985+ fn decode( & mut self , src: & mut BytesMut ) -> Result <Option <$t>, Self :: Error > {
986+ self . 0 . decode( src)
987+ }
988+ }
989+
990+ impl_deferred_sync!( $name) ;
991+ impl_deferred_sync!( CoreVecDecoder <$name>) ;
992+ } ;
993+ }
994+
995+ impl_canonical_nan_codec ! ( CanonicalNanF32Codec , F32Codec , f32 , CANONICAL_NAN_F32 ) ;
996+ impl_canonical_nan_codec ! ( CanonicalNanF64Codec , F64Codec , f64 , CANONICAL_NAN_F64 ) ;
997+
934998impl_copy_codec ! ( bool , BoolCodec ) ;
935999impl_copy_codec ! ( i8 , S8Codec ) ;
9361000impl_copy_codec ! ( i16 , S16Codec ) ;
@@ -939,8 +1003,8 @@ impl_copy_codec!(i32, S32Codec);
9391003impl_copy_codec ! ( u32 , U32Codec ) ;
9401004impl_copy_codec ! ( i64 , S64Codec ) ;
9411005impl_copy_codec ! ( u64 , U64Codec ) ;
942- impl_copy_codec ! ( f32 , F32Codec ) ;
943- impl_copy_codec ! ( f64 , F64Codec ) ;
1006+ impl_copy_codec ! ( f32 , CanonicalNanF32Codec ) ;
1007+ impl_copy_codec ! ( f64 , CanonicalNanF64Codec ) ;
9441008impl_copy_codec ! ( char , Utf8Codec ) ;
9451009
9461010impl < T > Encode < T > for u8 {
@@ -2295,4 +2359,43 @@ mod tests {
22952359 assert_eq ! ( buf. as_ref( ) , b"\x42 \x42 " ) ;
22962360 Ok ( ( ) )
22972361 }
2362+
2363+ #[ test]
2364+ fn canonical_nan_f32 ( ) {
2365+ let mut enc = <f32 as Encode < NoopStream > >:: Encoder :: default ( ) ;
2366+
2367+ // A non-canonical (e.g. signalling) `NaN` is canonicalized on encode.
2368+ let mut buf = BytesMut :: new ( ) ;
2369+ enc. encode ( f32:: from_bits ( 0x7f80_0001 ) , & mut buf) . unwrap ( ) ;
2370+ assert_eq ! ( buf. as_ref( ) , CANONICAL_NAN_F32 . to_le_bytes( ) ) ;
2371+
2372+ // A negative `NaN` is canonicalized to the (positive) canonical `NaN`.
2373+ let mut buf = BytesMut :: new ( ) ;
2374+ enc. encode ( f32:: from_bits ( 0xffc0_0000 ) , & mut buf) . unwrap ( ) ;
2375+ assert_eq ! ( buf. as_ref( ) , CANONICAL_NAN_F32 . to_le_bytes( ) ) ;
2376+
2377+ // Non-`NaN` values are encoded unchanged.
2378+ let mut buf = BytesMut :: new ( ) ;
2379+ enc. encode ( 1.5_f32 , & mut buf) . unwrap ( ) ;
2380+ assert_eq ! ( buf. as_ref( ) , 1.5_f32 . to_bits( ) . to_le_bytes( ) ) ;
2381+ }
2382+
2383+ #[ test]
2384+ fn canonical_nan_f64 ( ) {
2385+ let mut enc = <f64 as Encode < NoopStream > >:: Encoder :: default ( ) ;
2386+
2387+ let mut buf = BytesMut :: new ( ) ;
2388+ enc. encode ( f64:: from_bits ( 0x7ff0_0000_0000_0001 ) , & mut buf)
2389+ . unwrap ( ) ;
2390+ assert_eq ! ( buf. as_ref( ) , CANONICAL_NAN_F64 . to_le_bytes( ) ) ;
2391+
2392+ let mut buf = BytesMut :: new ( ) ;
2393+ enc. encode ( f64:: from_bits ( 0xfff8_0000_0000_0000 ) , & mut buf)
2394+ . unwrap ( ) ;
2395+ assert_eq ! ( buf. as_ref( ) , CANONICAL_NAN_F64 . to_le_bytes( ) ) ;
2396+
2397+ let mut buf = BytesMut :: new ( ) ;
2398+ enc. encode ( 1.5_f64 , & mut buf) . unwrap ( ) ;
2399+ assert_eq ! ( buf. as_ref( ) , 1.5_f64 . to_bits( ) . to_le_bytes( ) ) ;
2400+ }
22982401}
0 commit comments