[Bf-blender-cvs] [a10a7f42de2] master: PyAPI: Integer conversion functions

Campbell Barton noreply at git.blender.org
Sun Aug 20 07:45:36 CEST 2017


Commit: a10a7f42de29d251e27f6b85e7f3cac26e6843ed
Author: Campbell Barton
Date:   Sun Aug 20 15:39:08 2017 +1000
Branches: master
https://developer.blender.org/rBa10a7f42de29d251e27f6b85e7f3cac26e6843ed

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.

===================================================================

M	source/blender/python/generic/py_capi_utils.c
M	source/blender/python/generic/py_capi_utils.h

===================================================================

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__ */



More information about the Bf-blender-cvs mailing list