summaryrefslogtreecommitdiff
path: root/Cython/Utility/TypeConversion.c
diff options
context:
space:
mode:
Diffstat (limited to 'Cython/Utility/TypeConversion.c')
-rw-r--r--Cython/Utility/TypeConversion.c117
1 files changed, 107 insertions, 10 deletions
diff --git a/Cython/Utility/TypeConversion.c b/Cython/Utility/TypeConversion.c
index 382bcbf27..3e730e0fb 100644
--- a/Cython/Utility/TypeConversion.c
+++ b/Cython/Utility/TypeConversion.c
@@ -1097,33 +1097,130 @@ static CYTHON_INLINE {{TYPE}} {{FROM_PY_FUNCTION}}(PyObject *x) {
#endif
}
}
+
+ {{if IS_ENUM}}
+ PyErr_SetString(PyExc_RuntimeError,
+ "_PyLong_AsByteArray() not available, cannot convert large enums");
+ return ({{TYPE}}) -1;
+ {{else}}
+ // large integer type and no access to PyLong internals => allow for a more expensive conversion
{
-#if (CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) && !defined(_PyLong_AsByteArray)
- PyErr_SetString(PyExc_RuntimeError,
- "_PyLong_AsByteArray() not available, cannot convert large numbers");
-#else
{{TYPE}} val;
PyObject *v = __Pyx_PyNumber_IntOrLong(x);
- #if PY_MAJOR_VERSION < 3
+#if PY_MAJOR_VERSION < 3
if (likely(v) && !PyLong_Check(v)) {
PyObject *tmp = v;
v = PyNumber_Long(tmp);
Py_DECREF(tmp);
}
- #endif
+#endif
if (likely(v)) {
+ int ret = -1;
+#if !(CYTHON_COMPILING_IN_PYPY || CYTHON_COMPILING_IN_LIMITED_API) || defined(_PyLong_AsByteArray)
int one = 1; int is_little = (int)*(unsigned char *)&one;
unsigned char *bytes = (unsigned char *)&val;
- int ret = _PyLong_AsByteArray((PyLongObject *)v,
- bytes, sizeof(val),
- is_little, !is_unsigned);
+ ret = _PyLong_AsByteArray((PyLongObject *)v,
+ bytes, sizeof(val),
+ is_little, !is_unsigned);
+#else
+// Inefficient copy of bit chunks through the C-API. Probably still better than a "cannot do this" exception.
+ PyObject *stepval = NULL, *mask = NULL, *shift = NULL;
+ int bits, remaining_bits, is_negative = 0;
+ long idigit;
+ int chunk_size = (sizeof(long) < 8) ? 30 : 62;
+
+ // use exact PyLong to prevent user defined &&/<</etc. implementations
+ if (unlikely(!PyLong_CheckExact(v))) {
+ PyObject *tmp = v;
+ v = PyNumber_Long(v);
+ assert(PyLong_CheckExact(v));
+ Py_DECREF(tmp);
+ if (unlikely(!v)) return ({{TYPE}}) -1;
+ }
+
+#if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000
+ if (Py_SIZE(x) == 0)
+ return ({{TYPE}}) 0;
+ is_negative = Py_SIZE(x) < 0;
+#else
+ {
+ // misuse Py_False as a quick way to compare to a '0' int object
+ int result = PyObject_RichCompareBool(x, Py_False, Py_LT);
+ if (unlikely(result < 0))
+ return ({{TYPE}}) -1;
+ is_negative = result == 1;
+ }
+#endif
+
+ if (is_unsigned && unlikely(is_negative)) {
+ goto raise_neg_overflow;
+ } else if (is_negative) {
+ // bit-invert to make sure we can safely convert it
+ stepval = PyNumber_Invert(v);
+ if (unlikely(!stepval))
+ return ({{TYPE}}) -1;
+ } else {
+ stepval = __Pyx_NewRef(v);
+ }
+
+ // unpack full chunks of bits
+ val = ({{TYPE}}) 0;
+ mask = PyLong_FromLong((1L << chunk_size) - 1); if (unlikely(!mask)) goto done;
+ shift = PyLong_FromLong(chunk_size); if (unlikely(!shift)) goto done;
+ for (bits = 0; bits < (int) sizeof({{TYPE}}) * 8 - chunk_size; bits += chunk_size) {
+ PyObject *tmp, *digit;
+
+ digit = PyNumber_And(stepval, mask);
+ if (unlikely(!digit)) goto done;
+ idigit = PyLong_AsLong(digit);
+ Py_DECREF(digit);
+ if (unlikely(idigit < 0)) goto done;
+
+ tmp = PyNumber_Rshift(stepval, shift);
+ if (unlikely(!tmp)) goto done;
+ Py_DECREF(stepval); stepval = tmp;
+
+ val |= (({{TYPE}}) idigit) << bits;
+
+ #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000
+ if (Py_SIZE(stepval) == 0)
+ goto unpacking_done;
+ #endif
+ }
+
+ // detect overflow when adding the last bits
+ idigit = PyLong_AsLong(stepval);
+ if (unlikely(idigit < 0)) goto done;
+ remaining_bits = ((int) sizeof({{TYPE}}) * 8) - bits - (is_unsigned ? 0 : 1);
+ if (unlikely(idigit >= (1L << remaining_bits)))
+ goto raise_overflow;
+ val |= (({{TYPE}}) idigit) << bits;
+
+ #if CYTHON_COMPILING_IN_LIMITED_API && PY_VERSION_HEX < 0x030B0000
+ unpacking_done:
+ #endif
+ // handle sign and overflow into sign bit
+ if (!is_unsigned) {
+ // gcc warns about unsigned (val < 0) => test sign bit instead
+ if (unlikely(val & ((({{TYPE}}) 1) << (sizeof({{TYPE}}) * 8 - 1))))
+ goto raise_overflow;
+ // undo the PyNumber_Invert() above
+ if (is_negative)
+ val = ~val;
+ }
+ ret = 0;
+ done:
+ Py_XDECREF(shift);
+ Py_XDECREF(mask);
+ Py_XDECREF(stepval);
+#endif
Py_DECREF(v);
if (likely(!ret))
return val;
}
-#endif
return ({{TYPE}}) -1;
}
+ {{endif}}
} else {
{{TYPE}} val;
PyObject *tmp = __Pyx_PyNumber_IntOrLong(x);