Line data Source code
1 : #ifndef HEADER_fd_src_util_bits_fd_sat_h
2 : #define HEADER_fd_src_util_bits_fd_sat_h
3 :
4 : #include "fd_bits.h"
5 :
6 : /* Set of primitives for saturating math operations, mimicking the behaviour
7 : of Rust's primitive `saturating_add`, `saturating_sub`, `saturating_mul` operations.
8 : These saturate at the boundaries of the integer representation, instead of overflowing
9 : or underflowing.
10 :
11 : Note that this is a placeholder API, and the implementations will be optimised and
12 : hardened in the future. The intent of this is to provide an abstraction for saturating
13 : operations which can be used throughout the codebase, providing a single place to optimize
14 : these. */
15 :
16 : FD_PROTOTYPES_BEGIN
17 :
18 : FD_FN_CONST static inline uint128
19 300013284 : fd_uint128_sat_add( uint128 x, uint128 y ) {
20 300013284 : uint128 res = x + y;
21 300013284 : return fd_uint128_if( res < x, UINT128_MAX, res );
22 300013284 : }
23 :
24 : FD_FN_CONST static inline uint128
25 318009027 : fd_uint128_sat_mul( uint128 x, uint128 y ) {
26 318009027 : uint128 res = x * y;
27 318009027 : uchar overflow = ( x != 0 ) && ( y != 0 ) && ( ( res < x ) || ( res < y ) || ( ( res / x ) != y ) );
28 318009027 : return fd_uint128_if( overflow, UINT128_MAX, res );
29 318009027 : }
30 :
31 : FD_FN_CONST static inline uint128
32 300000006 : fd_uint128_sat_sub( uint128 x, uint128 y ) {
33 300000006 : uint128 res = x - y;
34 300000006 : return fd_uint128_if( res > x, 0, res );
35 300000006 : }
36 :
37 : FD_FN_CONST static inline ulong
38 300419706 : fd_ulong_sat_add( ulong x, ulong y ) {
39 300419706 : ulong res;
40 300419706 : int cf = __builtin_uaddl_overflow ( x, y, &res );
41 300419706 : return fd_ulong_if( cf, ULONG_MAX, res );
42 300419706 : }
43 :
44 : FD_FN_CONST static inline ulong
45 300076926 : fd_ulong_sat_mul( ulong x, ulong y ) {
46 300076926 : ulong res;
47 300076926 : int cf = __builtin_umull_overflow ( x, y, &res );
48 300076926 : return fd_ulong_if( cf, ULONG_MAX, res );
49 300076926 : }
50 :
51 : FD_FN_CONST static inline ulong
52 300122913 : fd_ulong_sat_sub( ulong x, ulong y ) {
53 300122913 : ulong res;
54 300122913 : int cf = __builtin_usubl_overflow ( x, y, &res );
55 300122913 : return fd_ulong_if( cf, 0UL, res );
56 300122913 : }
57 :
58 : FD_FN_CONST static inline long
59 300065670 : fd_long_sat_add( long x, long y ) {
60 300065670 : long res;
61 300065670 : int cf = __builtin_saddl_overflow ( x, y, &res );
62 : /* https://stackoverflow.com/a/56531252
63 : x + y overflows => x, y have the same sign
64 : we can use either to determine the result,
65 : with the trick described in the SO answer.
66 : We chose x because it works also for sub.
67 : It is not UB because we compile with -fwrapv. */
68 300065670 : return fd_long_if( cf, (long)((ulong)x >> 63) + LONG_MAX, res );
69 300065670 : }
70 :
71 : FD_FN_CONST static inline long
72 300146809 : fd_long_sat_sub( long x, long y ) {
73 300146809 : long res;
74 300146809 : int cf = __builtin_ssubl_overflow ( x, y, &res );
75 300146809 : return fd_long_if( cf, (long)((ulong)x >> 63) + LONG_MAX, res );
76 300146809 : }
77 :
78 : FD_FN_CONST static inline long
79 300012159 : fd_long_sat_mul( long x, long y ) {
80 300012159 : long res;
81 300012159 : int cf = __builtin_smull_overflow ( x, y, &res );
82 300012159 : return fd_long_if( cf, (long)((ulong)(x ^ y) >> 63) + LONG_MAX, res );
83 300012159 : }
84 :
85 : FD_FN_CONST static inline uint
86 300003036 : fd_uint_sat_add( uint x, uint y ) {
87 300003036 : uint res;
88 300003036 : int cf = __builtin_uadd_overflow ( x, y, &res );
89 300003036 : return fd_uint_if( cf, UINT_MAX, res );
90 300003036 : }
91 :
92 : FD_FN_CONST static inline uint
93 300001863 : fd_uint_sat_mul( uint x, uint y ) {
94 300001863 : uint res;
95 300001863 : int cf = __builtin_umul_overflow ( x, y, &res );
96 300001863 : return fd_uint_if( cf, UINT_MAX, res );
97 300001863 : }
98 :
99 : FD_FN_CONST static inline uint
100 300001578 : fd_uint_sat_sub( uint x, uint y ) {
101 300001578 : uint res;
102 300001578 : int cf = __builtin_usub_overflow ( x, y, &res );
103 300001578 : return fd_uint_if( cf, 0U, res );
104 300001578 : }
105 :
106 : FD_FN_CONST static inline ushort
107 0 : fd_ushort_sat_add( ushort x, ushort y ) {
108 0 : uint res = (uint)x + (uint)y;
109 0 : return fd_ushort_if( res>USHORT_MAX, USHORT_MAX, (ushort)res );
110 0 : }
111 :
112 : FD_FN_CONST static inline ushort
113 3 : fd_ushort_sat_mul( ushort x, ushort y ) {
114 3 : uint res = (uint)x * (uint)y;
115 3 : return fd_ushort_if( res>USHORT_MAX, USHORT_MAX, (ushort)res );
116 3 : }
117 :
118 : FD_FN_CONST static inline double
119 0 : fd_double_sat_add( double x, double y ) {
120 0 : // What does rust do here?
121 0 : return x + y;
122 0 : }
123 :
124 : FD_FN_CONST static inline double
125 0 : fd_double_sat_mul( double x, double y ) {
126 0 : // What does rust do here?
127 0 : return x * y;
128 0 : }
129 :
130 : FD_FN_CONST static inline double
131 0 : fd_double_sat_sub( double x, double y ) {
132 0 : // What does rust do here?
133 0 : return x - y;
134 0 : }
135 :
136 : FD_PROTOTYPES_END
137 :
138 : #endif /* HEADER_fd_src_util_bits_fd_sat_h */
|