PyAPI: Integer conversion functions

Python's C-API doesn't provide functions to get
int's at specific integer sizes.

Leaving the caller to check for overflow,
which ended up being ignored in practice.

Add API functions that convert int/uint 8/16/32/64, also bool.
Raising overflow exception for unsupported ranges.
This commit is contained in:
2017-08-20 15:39:08 +10:00
parent 2ff9c8a3bc
commit a10a7f42de
2 changed files with 123 additions and 1 deletions

View File

@@ -85,7 +85,7 @@ int PyC_AsArray_FAST(
/* could use is_double for 'long int' but no use now */
int *array_int = array;
for (i = 0; i < length; i++) {
array_int[i] = PyLong_AsLong(value_fast_items[i]);
array_int[i] = PyC_Long_AsI32(value_fast_items[i]);
}
}
else if (type == &PyBool_Type) {
@@ -203,6 +203,8 @@ void PyC_List_Fill(PyObject *list, PyObject *value)
/**
* Use with PyArg_ParseTuple's "O&" formatting.
*
* \see #PyC_Long_AsBool for a similar function to use outside of argument parsing.
*/
int PyC_ParseBool(PyObject *o, void *p)
{
@@ -1115,3 +1117,101 @@ bool PyC_RunString_AsString(const char *expr, const char *filename, char **r_val
}
#endif /* #ifndef MATH_STANDALONE */
/* -------------------------------------------------------------------- */
/** \name Int Conversion
*
* \note Python doesn't provide overflow checks for specific bit-widths.
*
* \{ */
/* Compiler optimizes out redundant checks. */
#ifdef __GNUC__
# pragma warning(push)
# pragma GCC diagnostic ignored "-Wtype-limits"
#endif
/**
* Don't use `bool` return type, so -1 can be used as an error value.
*/
int PyC_Long_AsBool(PyObject *value)
{
int test = _PyLong_AsInt(value);
if (UNLIKELY((uint)test > 1)) {
PyErr_SetString(PyExc_TypeError,
"Python number not a bool (0/1)");
return -1;
}
return test;
}
int8_t PyC_Long_AsI8(PyObject *value)
{
int test = _PyLong_AsInt(value);
if (UNLIKELY(test < INT8_MIN || test > INT8_MAX)) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C int8");
return -1;
}
return (int8_t)test;
}
int16_t PyC_Long_AsI16(PyObject *value)
{
int test = _PyLong_AsInt(value);
if (UNLIKELY(test < INT16_MIN || test > INT16_MAX)) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C int16");
return -1;
}
return (int16_t)test;
}
/* Inlined in header:
* PyC_Long_AsI32
* PyC_Long_AsI64
*/
uint8_t PyC_Long_AsU8(PyObject *value)
{
ulong test = PyLong_AsUnsignedLong(value);
if (UNLIKELY(test > UINT8_MAX)) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C uint8");
return (uint8_t)-1;
}
return (uint8_t)test;
}
uint16_t PyC_Long_AsU16(PyObject *value)
{
ulong test = PyLong_AsUnsignedLong(value);
if (UNLIKELY(test > UINT16_MAX)) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C uint16");
return (uint16_t)-1;
}
return (uint16_t)test;
}
uint32_t PyC_Long_AsU32(PyObject *value)
{
ulong test = PyLong_AsUnsignedLong(value);
if (UNLIKELY(test > UINT32_MAX)) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C uint32");
return (uint32_t)-1;
}
return (uint32_t)test;
}
/* Inlined in header:
* PyC_Long_AsU64
*/
#ifdef __GNUC__
# pragma warning(pop)
#endif
/** \} */