@@ -3830,6 +3830,9 @@ x_sub(PyLongObject *a, PyLongObject *b)
38303830 return maybe_small_long (long_normalize (z ));
38313831}
38323832
3833+ static PyLongObject * _PyCompactLong_AddWide (PyLongObject * a , PyLongObject * b );
3834+ static PyLongObject * _PyCompactLong_SubtractWide (PyLongObject * a , PyLongObject * b );
3835+
38333836static PyLongObject *
38343837long_add (PyLongObject * a , PyLongObject * b )
38353838{
@@ -3838,6 +3841,11 @@ long_add(PyLongObject *a, PyLongObject *b)
38383841 return _PyLong_FromSTwoDigits (z );
38393842 }
38403843
3844+ PyLongObject * fast = _PyCompactLong_AddWide (a , b );
3845+ if (fast != NULL || PyErr_Occurred ()) {
3846+ return fast ;
3847+ }
3848+
38413849 PyLongObject * z ;
38423850 if (_PyLong_IsNegative (a )) {
38433851 if (_PyLong_IsNegative (b )) {
@@ -3871,6 +3879,124 @@ _PyCompactLong_Add(PyLongObject *a, PyLongObject *b)
38713879 return medium_from_stwodigits (v );
38723880}
38733881
3882+ /* Max number of digits a PyLong can have and still fit in int64_t.
3883+ * 30-bit builds: ceil(64/30) = 3. 15-bit builds: ceil(64/15) = 5. */
3884+ #define _PY_LONG_MAX_DIGITS_FOR_INT64 ((64 + PyLong_SHIFT - 1) / PyLong_SHIFT)
3885+
3886+ static inline bool
3887+ _PyLong_TryAsInt64ExactLocal (PyLongObject * v , int64_t * out )
3888+ {
3889+ assert (PyLong_CheckExact ((PyObject * )v ));
3890+ uintptr_t tag = v -> long_value .lv_tag ;
3891+ int sign = 1 - (int )(tag & SIGN_MASK );
3892+ if (tag < (2u << NON_SIZE_BITS )) {
3893+ * out = (int64_t )(sign * (Py_ssize_t )v -> long_value .ob_digit [0 ]);
3894+ return true;
3895+ }
3896+ Py_ssize_t ndigits = (Py_ssize_t )(tag >> NON_SIZE_BITS );
3897+ if (ndigits > _PY_LONG_MAX_DIGITS_FOR_INT64 ) {
3898+ return false;
3899+ }
3900+ uint64_t abs_val = 0 ;
3901+ #if PyLong_SHIFT == 30
3902+ if (ndigits == 2 ) {
3903+ abs_val = (uint64_t )v -> long_value .ob_digit [0 ] |
3904+ ((uint64_t )v -> long_value .ob_digit [1 ] << 30 );
3905+ * out = sign < 0 ? - (int64_t )abs_val : (int64_t )abs_val ;
3906+ return true;
3907+ }
3908+ #endif
3909+ unsigned int shift = 0 ;
3910+ for (Py_ssize_t i = 0 ; i < ndigits - 1 ; i ++ ) {
3911+ abs_val |= (uint64_t )v -> long_value .ob_digit [i ] << shift ;
3912+ shift += PyLong_SHIFT ;
3913+ }
3914+ uint64_t top = (uint64_t )v -> long_value .ob_digit [ndigits - 1 ];
3915+ if (ndigits == _PY_LONG_MAX_DIGITS_FOR_INT64 && (top >> (64 - shift )) != 0 ) {
3916+ return false;
3917+ }
3918+ abs_val |= top << shift ;
3919+ if (abs_val <= (uint64_t )INT64_MAX ) {
3920+ * out = sign < 0 ? - (int64_t )abs_val : (int64_t )abs_val ;
3921+ return true;
3922+ }
3923+ if (sign < 0 && abs_val == (uint64_t )INT64_MAX + 1 ) {
3924+ * out = INT64_MIN ;
3925+ return true;
3926+ }
3927+ return false;
3928+ }
3929+
3930+ static inline bool
3931+ _Py_i64_add_overflow (int64_t a , int64_t b , int64_t * out )
3932+ {
3933+ #if defined(__GNUC__ ) || defined(__clang__ )
3934+ return __builtin_add_overflow (a , b , out );
3935+ #else
3936+ if ((b > 0 && a > INT64_MAX - b ) || (b < 0 && a < INT64_MIN - b )) {
3937+ return true;
3938+ }
3939+ * out = a + b ;
3940+ return false;
3941+ #endif
3942+ }
3943+
3944+ static inline PyLongObject *
3945+ _wide_op_result (int64_t v )
3946+ {
3947+ if (IS_SMALL_INT (v )) {
3948+ return (PyLongObject * )Py_NewRef (get_small_int ((sdigit )v ));
3949+ }
3950+ assert (v != 0 );
3951+ if (is_medium_int (v )) {
3952+ PyLongObject * result = (PyLongObject * )_Py_FREELIST_POP (PyLongObject , ints );
3953+ if (result == NULL ) {
3954+ result = PyObject_Malloc (sizeof (PyLongObject ));
3955+ if (result == NULL ) {
3956+ return (PyLongObject * )PyErr_NoMemory ();
3957+ }
3958+ _PyObject_Init ((PyObject * )result , & PyLong_Type );
3959+ _PyLong_InitTag (result );
3960+ }
3961+ digit abs_v = v < 0 ? (digit )(- (sdigit )v ) : (digit )(sdigit )v ;
3962+ _PyLong_SetSignAndDigitCount (result , v < 0 ? -1 : 1 , 1 );
3963+ result -> long_value .ob_digit [0 ] = abs_v ;
3964+ return result ;
3965+ }
3966+ return (PyLongObject * )_PyLong_FromLarge (v );
3967+ }
3968+
3969+ /* Exact int -> int64_t helper for the wide int fast path.
3970+ * Keeps the exact-type check local to this translation unit. */
3971+ static inline bool
3972+ _PyLong_CheckExactAndTryAsInt64 (PyObject * op , int64_t * out )
3973+ {
3974+ return PyLong_CheckExact (op ) &&
3975+ _PyLong_TryAsInt64ExactLocal ((PyLongObject * )op , out );
3976+ }
3977+
3978+ /* Wide variant: operands are exact ints in the full int64 range (may be
3979+ * non-compact). Returns NULL without raising when an input is out of int64
3980+ * range or the sum overflows int64. */
3981+ static PyLongObject *
3982+ _PyCompactLong_AddWide (PyLongObject * a , PyLongObject * b )
3983+ {
3984+ if (_PyLong_BothAreCompact (a , b )) {
3985+ stwodigits v = medium_value (a ) + medium_value (b );
3986+ return _PyLong_FromSTwoDigits (v );
3987+ }
3988+ int64_t va , vb ;
3989+ if (!_PyLong_CheckExactAndTryAsInt64 ((PyObject * )a , & va ) ||
3990+ !_PyLong_CheckExactAndTryAsInt64 ((PyObject * )b , & vb )) {
3991+ return NULL ;
3992+ }
3993+ int64_t v ;
3994+ if (_Py_i64_add_overflow (va , vb , & v )) {
3995+ return NULL ;
3996+ }
3997+ return _wide_op_result (v );
3998+ }
3999+
38744000static PyObject *
38754001long_add_method (PyObject * a , PyObject * b )
38764002{
@@ -3886,6 +4012,11 @@ long_sub(PyLongObject *a, PyLongObject *b)
38864012 return _PyLong_FromSTwoDigits (medium_value (a ) - medium_value (b ));
38874013 }
38884014
4015+ PyLongObject * fast = _PyCompactLong_SubtractWide (a , b );
4016+ if (fast != NULL || PyErr_Occurred ()) {
4017+ return fast ;
4018+ }
4019+
38894020 PyLongObject * z ;
38904021 if (_PyLong_IsNegative (a )) {
38914022 if (_PyLong_IsNegative (b )) {
@@ -3916,6 +4047,39 @@ _PyCompactLong_Subtract(PyLongObject *a, PyLongObject *b)
39164047 return medium_from_stwodigits (v );
39174048}
39184049
4050+ static inline bool
4051+ _Py_i64_sub_overflow (int64_t a , int64_t b , int64_t * out )
4052+ {
4053+ #if defined(__GNUC__ ) || defined(__clang__ )
4054+ return __builtin_sub_overflow (a , b , out );
4055+ #else
4056+ if ((b < 0 && a > INT64_MAX + b ) || (b > 0 && a < INT64_MIN + b )) {
4057+ return true;
4058+ }
4059+ * out = a - b ;
4060+ return false;
4061+ #endif
4062+ }
4063+
4064+ static PyLongObject *
4065+ _PyCompactLong_SubtractWide (PyLongObject * a , PyLongObject * b )
4066+ {
4067+ if (_PyLong_BothAreCompact (a , b )) {
4068+ stwodigits v = medium_value (a ) - medium_value (b );
4069+ return _PyLong_FromSTwoDigits (v );
4070+ }
4071+ int64_t va , vb ;
4072+ if (!_PyLong_CheckExactAndTryAsInt64 ((PyObject * )a , & va ) ||
4073+ !_PyLong_CheckExactAndTryAsInt64 ((PyObject * )b , & vb )) {
4074+ return NULL ;
4075+ }
4076+ int64_t v ;
4077+ if (_Py_i64_sub_overflow (va , vb , & v )) {
4078+ return NULL ;
4079+ }
4080+ return _wide_op_result (v );
4081+ }
4082+
39194083static PyObject *
39204084long_sub_method (PyObject * a , PyObject * b )
39214085{
0 commit comments