@@ -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:: from_ne_bytes( self . 0 ) ;
137+ $name( ( !self_native) . to_ne_bytes( ) , PhantomData )
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:: from_ne_bytes( self . 0 ) ;
190+ let rhs_native = $native:: from_ne_bytes( rhs. 0 ) ;
191+ let result_native = core:: ops:: $trait:: $method( self_native, rhs_native) ;
192+ $name( result_native. to_ne_bytes( ) , PhantomData )
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,114 @@ mod tests {
834964 call_for_all_types ! ( test_non_native_endian, NonNativeEndian ) ;
835965 }
836966
967+ #[ cfg_attr( test, test) ]
968+ fn test_ops_impls ( ) {
969+ // Test implementations of traits in `core::ops`. Some of these are
970+ // fairly banal, but some are optimized to perform the operation without
971+ // swapping byte order (namely, bit-wise operations which are identical
972+ // regardless of byte order). These are important to test, and while
973+ // we're testing those anyway, it's trivial to test all of the impls.
974+
975+ fn test < T , F , G , H > ( op : F , op_native : G , op_native_checked : Option < H > )
976+ where
977+ T : ByteOrderType ,
978+ F : Fn ( T , T ) -> T ,
979+ G : Fn ( T :: Native , T :: Native ) -> T :: Native ,
980+ H : Fn ( T :: Native , T :: Native ) -> Option < T :: Native > ,
981+ {
982+ let mut r = SmallRng :: seed_from_u64 ( RNG_SEED ) ;
983+ for _ in 0 ..RAND_ITERS {
984+ let n0 = T :: Native :: rand ( & mut r) ;
985+ let n1 = T :: Native :: rand ( & mut r) ;
986+ let t0 = T :: new ( n0) ;
987+ let t1 = T :: new ( n1) ;
988+
989+ // If this operation would overflow/underflow, skip it rather
990+ // than attempt to catch and recover from panics.
991+ if matches ! ( & op_native_checked, Some ( checked) if checked( n0, n1) . is_none( ) ) {
992+ continue ;
993+ }
994+
995+ let n_res = op_native ( n0, n1) ;
996+ let t_res = op ( t0, t1) ;
997+
998+ // For `f32` and `f64`, NaN values are not considered equal to
999+ // themselves. We store `Option<f32>`/`Option<f64>` and store
1000+ // NaN as `None` so they can still be compared.
1001+ let n_res = ( !T :: Native :: is_nan ( n_res) ) . then ( || n_res) ;
1002+ let t_res = ( !T :: Native :: is_nan ( t_res. get ( ) ) ) . then ( || t_res. get ( ) ) ;
1003+ assert_eq ! ( n_res, t_res) ;
1004+ }
1005+ }
1006+
1007+ macro_rules! test {
1008+ ( @binary $trait: ident, $method: ident $( [ $checked_method: ident] ) ?, $( $call_for_macros: ident) ,* ) => { {
1009+ test!(
1010+ @inner $trait,
1011+ core:: ops:: $trait:: $method,
1012+ core:: ops:: $trait:: $method,
1013+ {
1014+ #[ allow( unused_mut, unused_assignments) ]
1015+ let mut op_native_checked = None :: <fn ( T :: Native , T :: Native ) -> Option <T :: Native >>;
1016+ $(
1017+ op_native_checked = Some ( T :: Native :: $checked_method) ;
1018+ ) ?
1019+ op_native_checked
1020+ } ,
1021+ $( $call_for_macros) ,*
1022+ ) ;
1023+ } } ;
1024+ ( @unary $trait: ident, $method: ident $( [ $checked_method: ident] ) ?, $( $call_for_macros: ident) ,* ) => { {
1025+ test!(
1026+ @inner $trait,
1027+ |slf, _rhs| core:: ops:: $trait:: $method( slf) ,
1028+ |slf, _rhs| core:: ops:: $trait:: $method( slf) ,
1029+ {
1030+ #[ allow( unused_mut, unused_assignments) ]
1031+ let mut op_native_checked = None :: <fn ( T :: Native , T :: Native ) -> Option <T :: Native >>;
1032+ $(
1033+ op_native_checked = Some ( |slf, _rhs| T :: Native :: $checked_method( slf) ) ;
1034+ ) ?
1035+ op_native_checked
1036+ } ,
1037+ $( $call_for_macros) ,*
1038+ ) ;
1039+ } } ;
1040+ ( @inner $trait: ident, $op: expr, $op_native: expr, $op_native_checked: expr, $( $call_for_macros: ident) ,* ) => { {
1041+ fn t<T : ByteOrderType + core:: ops:: $trait<Output = T >>( )
1042+ where
1043+ T :: Native : core:: ops:: $trait<Output = T :: Native >,
1044+ {
1045+ test:: <T , _, _, _>(
1046+ $op,
1047+ $op_native,
1048+ $op_native_checked,
1049+ ) ;
1050+ }
1051+
1052+ $(
1053+ $call_for_macros!( t, NativeEndian ) ;
1054+ $call_for_macros!( t, NonNativeEndian ) ;
1055+ ) *
1056+ } } ;
1057+ }
1058+
1059+ test ! ( @binary Add , add[ checked_add] , call_for_all_types) ;
1060+ test ! ( @binary Div , div[ checked_div] , call_for_all_types) ;
1061+ test ! ( @binary Mul , mul[ checked_mul] , call_for_all_types) ;
1062+ test ! ( @binary Rem , rem[ checked_rem] , call_for_all_types) ;
1063+ test ! ( @binary Sub , sub[ checked_sub] , call_for_all_types) ;
1064+
1065+ test ! ( @binary BitAnd , bitand, call_for_unsigned_types, call_for_signed_types) ;
1066+ test ! ( @binary BitOr , bitor, call_for_unsigned_types, call_for_signed_types) ;
1067+ test ! ( @binary BitXor , bitxor, call_for_unsigned_types, call_for_signed_types) ;
1068+ test ! ( @binary Shl , shl[ checked_shl] , call_for_unsigned_types, call_for_signed_types) ;
1069+ test ! ( @binary Shr , shr[ checked_shr] , call_for_unsigned_types, call_for_signed_types) ;
1070+
1071+ test ! ( @unary Not , not, call_for_signed_types, call_for_unsigned_types) ;
1072+ test ! ( @unary Neg , neg, call_for_signed_types, call_for_float_types) ;
1073+ }
1074+
8371075 #[ test]
8381076 fn test_debug_impl ( ) {
8391077 // Ensure that Debug applies format options to the inner value.
0 commit comments