@@ -93,13 +93,12 @@ macro_rules! impl_fmt_traits {
9393 impl_fmt_trait!( $name, $native, Display ) ;
9494 } ;
9595 ( $name: ident, $native: ident, "unsigned integer" ) => {
96- impl_fmt_traits!( $name, $native, @all_traits ) ;
96+ impl_fmt_traits!( $name, $native, @all_types ) ;
9797 } ;
9898 ( $name: ident, $native: ident, "signed integer" ) => {
99- impl_fmt_traits!( $name, $native, @all_traits ) ;
99+ impl_fmt_traits!( $name, $native, @all_types ) ;
100100 } ;
101-
102- ( $name: ident, $native: ident, @all_traits) => {
101+ ( $name: ident, $native: ident, @all_types) => {
103102 impl_fmt_trait!( $name, $native, Display ) ;
104103 impl_fmt_trait!( $name, $native, Octal ) ;
105104 impl_fmt_trait!( $name, $native, LowerHex ) ;
@@ -108,6 +107,101 @@ macro_rules! impl_fmt_traits {
108107 } ;
109108}
110109
110+ macro_rules! impl_ops_traits {
111+ ( $name: ident, $native: ident, "floating point number" ) => {
112+ impl_ops_traits!( $name, $native, @all_types) ;
113+ impl_ops_traits!( $name, $native, @signed_integer_floating_point) ;
114+ } ;
115+ ( $name: ident, $native: ident, "unsigned integer" ) => {
116+ impl_ops_traits!( $name, $native, @signed_unsigned_integer) ;
117+ impl_ops_traits!( $name, $native, @all_types) ;
118+ } ;
119+ ( $name: ident, $native: ident, "signed integer" ) => {
120+ impl_ops_traits!( $name, $native, @signed_unsigned_integer) ;
121+ impl_ops_traits!( $name, $native, @signed_integer_floating_point) ;
122+ impl_ops_traits!( $name, $native, @all_types) ;
123+ } ;
124+ ( $name: ident, $native: ident, @signed_unsigned_integer) => {
125+ impl_ops_traits!( @without_byteorder_swap $name, $native, BitAnd , bitand, BitAndAssign , bitand_assign) ;
126+ impl_ops_traits!( @without_byteorder_swap $name, $native, BitOr , bitor, BitOrAssign , bitor_assign) ;
127+ impl_ops_traits!( @without_byteorder_swap $name, $native, BitXor , bitxor, BitXorAssign , bitxor_assign) ;
128+ impl_ops_traits!( @with_byteorder_swap $name, $native, Shl , shl, ShlAssign , shl_assign) ;
129+ impl_ops_traits!( @with_byteorder_swap $name, $native, Shr , shr, ShrAssign , shr_assign) ;
130+
131+ impl <O > core:: ops:: Not for $name<O > {
132+ type Output = $name<O >;
133+
134+ #[ inline( always) ]
135+ fn not( self ) -> $name<O > {
136+ let self_native: $native = transmute!( self ) ;
137+ transmute!( !self_native)
138+ }
139+ }
140+ } ;
141+ ( $name: ident, $native: ident, @signed_integer_floating_point) => {
142+ impl <O : ByteOrder > core:: ops:: Neg for $name<O > {
143+ type Output = $name<O >;
144+
145+ #[ inline( always) ]
146+ fn neg( self ) -> $name<O > {
147+ let self_native: $native = self . get( ) ;
148+ #[ allow( clippy:: arithmetic_side_effects) ]
149+ $name:: <O >:: new( -self_native)
150+ }
151+ }
152+ } ;
153+ ( $name: ident, $native: ident, @all_types) => {
154+ impl_ops_traits!( @with_byteorder_swap $name, $native, Add , add, AddAssign , add_assign) ;
155+ impl_ops_traits!( @with_byteorder_swap $name, $native, Div , div, DivAssign , div_assign) ;
156+ impl_ops_traits!( @with_byteorder_swap $name, $native, Mul , mul, MulAssign , mul_assign) ;
157+ impl_ops_traits!( @with_byteorder_swap $name, $native, Rem , rem, RemAssign , rem_assign) ;
158+ impl_ops_traits!( @with_byteorder_swap $name, $native, Sub , sub, SubAssign , sub_assign) ;
159+ } ;
160+ ( @with_byteorder_swap $name: ident, $native: ident, $trait: ident, $method: ident, $trait_assign: ident, $method_assign: ident) => {
161+ impl <O : ByteOrder > core:: ops:: $trait for $name<O > {
162+ type Output = $name<O >;
163+
164+ #[ inline( always) ]
165+ fn $method( self , rhs: $name<O >) -> $name<O > {
166+ let self_native: $native = self . get( ) ;
167+ let rhs_native: $native = rhs. get( ) ;
168+ let result_native = core:: ops:: $trait:: $method( self_native, rhs_native) ;
169+ $name:: <O >:: new( result_native)
170+ }
171+ }
172+
173+ impl <O : ByteOrder > core:: ops:: $trait_assign for $name<O > {
174+ #[ inline( always) ]
175+ fn $method_assign( & mut self , rhs: $name<O >) {
176+ * self = core:: ops:: $trait:: $method( * self , rhs) ;
177+ }
178+ }
179+ } ;
180+ // Implement traits in terms of the same trait on the native type, but
181+ // without performing a byte order swap. This only works for bitwise
182+ // operations like `&`, `|`, etc.
183+ ( @without_byteorder_swap $name: ident, $native: ident, $trait: ident, $method: ident, $trait_assign: ident, $method_assign: ident) => {
184+ impl <O : ByteOrder > core:: ops:: $trait for $name<O > {
185+ type Output = $name<O >;
186+
187+ #[ inline( always) ]
188+ fn $method( self , rhs: $name<O >) -> $name<O > {
189+ let self_native: $native = transmute!( self ) ;
190+ let rhs_native: $native = transmute!( rhs) ;
191+ let result_native = core:: ops:: $trait:: $method( self_native, rhs_native) ;
192+ transmute!( result_native)
193+ }
194+ }
195+
196+ impl <O : ByteOrder > core:: ops:: $trait_assign for $name<O > {
197+ #[ inline( always) ]
198+ fn $method_assign( & mut self , rhs: $name<O >) {
199+ * self = core:: ops:: $trait:: $method( * self , rhs) ;
200+ }
201+ }
202+ } ;
203+ }
204+
111205macro_rules! doc_comment {
112206 ( $x: expr, $( $tt: tt) * ) => {
113207 #[ doc = $x]
@@ -347,6 +441,7 @@ example of how it can be used for parsing UDP packets.
347441 }
348442
349443 impl_fmt_traits!( $name, $native, $number_kind) ;
444+ impl_ops_traits!( $name, $native, $number_kind) ;
350445
351446 impl <O : ByteOrder > Debug for $name<O > {
352447 #[ inline]
@@ -566,6 +661,14 @@ mod tests {
566661 }
567662
568663 fn is_nan ( self ) -> bool ;
664+
665+ fn checked_add ( self , rhs : Self ) -> Option < Self > ;
666+ fn checked_div ( self , rhs : Self ) -> Option < Self > ;
667+ fn checked_mul ( self , rhs : Self ) -> Option < Self > ;
668+ fn checked_rem ( self , rhs : Self ) -> Option < Self > ;
669+ fn checked_sub ( self , rhs : Self ) -> Option < Self > ;
670+ fn checked_shl ( self , rhs : Self ) -> Option < Self > ;
671+ fn checked_shr ( self , rhs : Self ) -> Option < Self > ;
569672 }
570673
571674 trait ByteArray :
@@ -618,7 +721,7 @@ mod tests {
618721 }
619722
620723 macro_rules! impl_traits {
621- ( $name: ident, $native: ident, $bytes: expr, $sign: ident $( , $is_nan : ident) ?) => {
724+ ( $name: ident, $native: ident, $bytes: expr, $sign: ident $( , @$float : ident) ?) => {
622725 impl Native for $native {
623726 // For some types, `0 as $native` is required (for example, when
624727 // `$native` is a floating-point type; `0` is an integer), but
@@ -631,9 +734,7 @@ mod tests {
631734 type Distribution = Standard ;
632735 const DIST : Standard = Standard ;
633736
634- fn is_nan( self ) -> bool {
635- false $( || self . $is_nan( ) ) ?
636- }
737+ impl_traits!( @float_dependent_methods $( @$float) ?) ;
637738 }
638739
639740 impl <O : ByteOrder > ByteOrderType for $name<O > {
@@ -665,6 +766,26 @@ mod tests {
665766
666767 impl_byte_order_type_unsigned!( $name, $sign) ;
667768 } ;
769+ ( @float_dependent_methods) => {
770+ fn is_nan( self ) -> bool { false }
771+ fn checked_add( self , rhs: Self ) -> Option <Self > { self . checked_add( rhs) }
772+ fn checked_div( self , rhs: Self ) -> Option <Self > { self . checked_div( rhs) }
773+ fn checked_mul( self , rhs: Self ) -> Option <Self > { self . checked_mul( rhs) }
774+ fn checked_rem( self , rhs: Self ) -> Option <Self > { self . checked_rem( rhs) }
775+ fn checked_sub( self , rhs: Self ) -> Option <Self > { self . checked_sub( rhs) }
776+ fn checked_shl( self , rhs: Self ) -> Option <Self > { self . checked_shl( rhs. try_into( ) . unwrap_or( u32 :: MAX ) ) }
777+ fn checked_shr( self , rhs: Self ) -> Option <Self > { self . checked_shr( rhs. try_into( ) . unwrap_or( u32 :: MAX ) ) }
778+ } ;
779+ ( @float_dependent_methods @float) => {
780+ fn is_nan( self ) -> bool { self . is_nan( ) }
781+ fn checked_add( self , rhs: Self ) -> Option <Self > { Some ( self + rhs) }
782+ fn checked_div( self , rhs: Self ) -> Option <Self > { Some ( self / rhs) }
783+ fn checked_mul( self , rhs: Self ) -> Option <Self > { Some ( self * rhs) }
784+ fn checked_rem( self , rhs: Self ) -> Option <Self > { Some ( self % rhs) }
785+ fn checked_sub( self , rhs: Self ) -> Option <Self > { Some ( self - rhs) }
786+ fn checked_shl( self , _rhs: Self ) -> Option <Self > { unimplemented!( ) }
787+ fn checked_shr( self , _rhs: Self ) -> Option <Self > { unimplemented!( ) }
788+ } ;
668789 }
669790
670791 impl_traits ! ( U16 , u16 , 2 , unsigned) ;
@@ -675,30 +796,39 @@ mod tests {
675796 impl_traits ! ( I32 , i32 , 4 , signed) ;
676797 impl_traits ! ( I64 , i64 , 8 , signed) ;
677798 impl_traits ! ( I128 , i128 , 16 , signed) ;
678- impl_traits ! ( F32 , f32 , 4 , signed, is_nan ) ;
679- impl_traits ! ( F64 , f64 , 8 , signed, is_nan ) ;
799+ impl_traits ! ( F32 , f32 , 4 , signed, @float ) ;
800+ impl_traits ! ( F64 , f64 , 8 , signed, @float ) ;
680801
681- macro_rules! call_for_all_types {
802+ macro_rules! call_for_unsigned_types {
682803 ( $fn: ident, $byteorder: ident) => {
683804 $fn:: <U16 <$byteorder>>( ) ;
684805 $fn:: <U32 <$byteorder>>( ) ;
685806 $fn:: <U64 <$byteorder>>( ) ;
686807 $fn:: <U128 <$byteorder>>( ) ;
808+ } ;
809+ }
810+
811+ macro_rules! call_for_signed_types {
812+ ( $fn: ident, $byteorder: ident) => {
687813 $fn:: <I16 <$byteorder>>( ) ;
688814 $fn:: <I32 <$byteorder>>( ) ;
689815 $fn:: <I64 <$byteorder>>( ) ;
690816 $fn:: <I128 <$byteorder>>( ) ;
817+ } ;
818+ }
819+
820+ macro_rules! call_for_float_types {
821+ ( $fn: ident, $byteorder: ident) => {
691822 $fn:: <F32 <$byteorder>>( ) ;
692823 $fn:: <F64 <$byteorder>>( ) ;
693824 } ;
694825 }
695826
696- macro_rules! call_for_unsigned_types {
827+ macro_rules! call_for_all_types {
697828 ( $fn: ident, $byteorder: ident) => {
698- $fn:: <U16 <$byteorder>>( ) ;
699- $fn:: <U32 <$byteorder>>( ) ;
700- $fn:: <U64 <$byteorder>>( ) ;
701- $fn:: <U128 <$byteorder>>( ) ;
829+ call_for_unsigned_types!( $fn, $byteorder) ;
830+ call_for_signed_types!( $fn, $byteorder) ;
831+ call_for_float_types!( $fn, $byteorder) ;
702832 } ;
703833 }
704834
@@ -834,6 +964,113 @@ mod tests {
834964 call_for_all_types ! ( test_non_native_endian, NonNativeEndian ) ;
835965 }
836966
967+ #[ cfg_attr( test, test) ]
968+ #[ cfg_attr( kani, kani:: proof) ]
969+ fn test_ops_impls ( ) {
970+ // Test implementations of traits in `core::ops`. Some of these are
971+ // fairly banal, but some are optimized to perform the operation without
972+ // swapping byte order (namely, bit-wise operations which are identical
973+ // regardless of byte order). These are important to test, and while
974+ // we're testing those anyway, it's trivial to test all of the impls.
975+
976+ fn test < T : ByteOrderType > (
977+ op : impl Fn ( T , T ) -> T ,
978+ op_native : impl Fn ( T :: Native , T :: Native ) -> T :: Native ,
979+ op_native_checked : Option < impl Fn ( T :: Native , T :: Native ) -> Option < T :: Native > > ,
980+ ) {
981+ let mut r = SmallRng :: seed_from_u64 ( RNG_SEED ) ;
982+ for _ in 0 ..RAND_ITERS {
983+ let n0 = T :: Native :: rand ( & mut r) ;
984+ let n1 = T :: Native :: rand ( & mut r) ;
985+ let t0 = T :: new ( n0) ;
986+ let t1 = T :: new ( n1) ;
987+
988+ // If this operation would overflow/underflow, skip it rather
989+ // than attempt to catch and recover from panics.
990+ if matches ! ( & op_native_checked, Some ( checked) if checked( n0, n1) . is_none( ) ) {
991+ continue ;
992+ }
993+
994+ let n_res = op_native ( n0, n1) ;
995+ let t_res = op ( t0, t1) ;
996+
997+ // For `f32` and `f64`, NaN values are not considered equal to
998+ // themselves. We store `Option<f32>`/`Option<f64>` and store
999+ // NaN as `None` so they can still be compared.
1000+ let n_res = ( !T :: Native :: is_nan ( n_res) ) . then ( || n_res) ;
1001+ let t_res = ( !T :: Native :: is_nan ( t_res. get ( ) ) ) . then ( || t_res. get ( ) ) ;
1002+ assert_eq ! ( n_res, t_res) ;
1003+ }
1004+ }
1005+
1006+ macro_rules! test {
1007+ ( @binary $trait: ident, $method: ident $( [ $checked_method: ident] ) ?, $( $call_for_macros: ident) ,* ) => { {
1008+ test!(
1009+ @inner $trait,
1010+ core:: ops:: $trait:: $method,
1011+ core:: ops:: $trait:: $method,
1012+ {
1013+ #[ allow( unused_mut, unused_assignments) ]
1014+ let mut op_native_checked = None :: <fn ( T :: Native , T :: Native ) -> Option <T :: Native >>;
1015+ $(
1016+ op_native_checked = Some ( T :: Native :: $checked_method) ;
1017+ ) ?
1018+ op_native_checked
1019+ } ,
1020+ $( $call_for_macros) ,*
1021+ ) ;
1022+ } } ;
1023+ ( @unary $trait: ident, $method: ident $( [ $checked_method: ident] ) ?, $( $call_for_macros: ident) ,* ) => { {
1024+ test!(
1025+ @inner $trait,
1026+ |slf, _rhs| core:: ops:: $trait:: $method( slf) ,
1027+ |slf, _rhs| core:: ops:: $trait:: $method( slf) ,
1028+ {
1029+ #[ allow( unused_mut, unused_assignments) ]
1030+ let mut op_native_checked = None :: <fn ( T :: Native , T :: Native ) -> Option <T :: Native >>;
1031+ $(
1032+ op_native_checked = Some ( |slf, _rhs| T :: Native :: $checked_method( slf) ) ;
1033+ ) ?
1034+ op_native_checked
1035+ } ,
1036+ $( $call_for_macros) ,*
1037+ ) ;
1038+ } } ;
1039+ ( @inner $trait: ident, $op: expr, $op_native: expr, $op_native_checked: expr, $( $call_for_macros: ident) ,* ) => { {
1040+ fn t<T : ByteOrderType + core:: ops:: $trait<Output = T >>( )
1041+ where
1042+ T :: Native : core:: ops:: $trait<Output = T :: Native >,
1043+ {
1044+ test:: <T >(
1045+ $op,
1046+ $op_native,
1047+ $op_native_checked,
1048+ ) ;
1049+ }
1050+
1051+ $(
1052+ $call_for_macros!( t, NativeEndian ) ;
1053+ $call_for_macros!( t, NonNativeEndian ) ;
1054+ ) *
1055+ } } ;
1056+ }
1057+
1058+ test ! ( @binary Add , add[ checked_add] , call_for_all_types) ;
1059+ test ! ( @binary Div , div[ checked_div] , call_for_all_types) ;
1060+ test ! ( @binary Mul , mul[ checked_mul] , call_for_all_types) ;
1061+ test ! ( @binary Rem , rem[ checked_rem] , call_for_all_types) ;
1062+ test ! ( @binary Sub , sub[ checked_sub] , call_for_all_types) ;
1063+
1064+ test ! ( @binary BitAnd , bitand, call_for_unsigned_types, call_for_signed_types) ;
1065+ test ! ( @binary BitOr , bitor, call_for_unsigned_types, call_for_signed_types) ;
1066+ test ! ( @binary BitXor , bitxor, call_for_unsigned_types, call_for_signed_types) ;
1067+ test ! ( @binary Shl , shl[ checked_shl] , call_for_unsigned_types, call_for_signed_types) ;
1068+ test ! ( @binary Shr , shr[ checked_shr] , call_for_unsigned_types, call_for_signed_types) ;
1069+
1070+ test ! ( @unary Not , not, call_for_signed_types, call_for_unsigned_types) ;
1071+ test ! ( @unary Neg , neg, call_for_signed_types, call_for_float_types) ;
1072+ }
1073+
8371074 #[ test]
8381075 fn test_debug_impl ( ) {
8391076 // Ensure that Debug applies format options to the inner value.
0 commit comments