Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCampbell Barton <ideasman42@gmail.com>2017-08-20 08:39:08 +0300
committerCampbell Barton <ideasman42@gmail.com>2017-08-20 08:39:08 +0300
commita10a7f42de29d251e27f6b85e7f3cac26e6843ed (patch)
treed831f64eac4e372ced2bbbbc448f0b06f3d360c6 /source/blender/python
parent2ff9c8a3bc51142a1d7ad19d8b772b58e4dcc890 (diff)
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.
Diffstat (limited to 'source/blender/python')
-rw-r--r--source/blender/python/generic/py_capi_utils.c102
-rw-r--r--source/blender/python/generic/py_capi_utils.h22
2 files changed, 123 insertions, 1 deletions
diff --git a/source/blender/python/generic/py_capi_utils.c b/source/blender/python/generic/py_capi_utils.c
index 861e2dbb0df..abc2da9e4c7 100644
--- a/source/blender/python/generic/py_capi_utils.c
+++ b/source/blender/python/generic/py_capi_utils.c
@@ -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
+
+/** \} */
diff --git a/source/blender/python/generic/py_capi_utils.h b/source/blender/python/generic/py_capi_utils.h
index 3f89e1d82a0..322e67f486d 100644
--- a/source/blender/python/generic/py_capi_utils.h
+++ b/source/blender/python/generic/py_capi_utils.h
@@ -85,4 +85,26 @@ bool PyC_RunString_AsString(const char *expr, const char *filename, char **r_val
int PyC_ParseBool(PyObject *o, void *p);
+
+/* Integer parsing (with overflow checks), -1 on error. */
+int PyC_Long_AsBool(PyObject *value);
+int8_t PyC_Long_AsI8(PyObject *value);
+int16_t PyC_Long_AsI16(PyObject *value);
+#if 0 /* inline */
+int32_t PyC_Long_AsI32(PyObject *value);
+int64_t PyC_Long_AsI64(PyObject *value);
+#endif
+
+uint8_t PyC_Long_AsU8(PyObject *value);
+uint16_t PyC_Long_AsU16(PyObject *value);
+uint32_t PyC_Long_AsU32(PyObject *value);
+#if 0 /* inline */
+uint64_t PyC_Long_AsU64(PyObject *value);
+#endif
+
+/* inline so type signatures match as expected */
+BLI_INLINE int32_t PyC_Long_AsI32(PyObject *value) { return (int32_t)_PyLong_AsInt(value); }
+BLI_INLINE int64_t PyC_Long_AsI64(PyObject *value) { return (int64_t)PyLong_AsLongLong(value); }
+BLI_INLINE uint64_t PyC_Long_AsU64(PyObject *value) { return (uint64_t)PyLong_AsUnsignedLongLong(value); }
+
#endif /* __PY_CAPI_UTILS_H__ */