Skip to content

Commit

Permalink
Add PyLong_FromUInt64() and PyLong_AsUInt64() (#114)
Browse files Browse the repository at this point in the history
  • Loading branch information
vstinner authored Oct 9, 2024
1 parent 38e2d32 commit 669c882
Show file tree
Hide file tree
Showing 4 changed files with 168 additions and 8 deletions.
41 changes: 33 additions & 8 deletions docs/api.rst
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,39 @@ Python 3.14

See `PyUnicodeWriter_Format() documentation <https://docs.python.org/dev/c-api/unicode.html#c.PyUnicodeWriter_Format>`__.

.. c:function:: int PyLong_AsInt32(PyObject *obj, int32_t *pvalue)

See `PyLong_AsInt32() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_AsInt32>`__.

.. c:function:: int PyLong_AsInt64(PyObject *obj, int64_t *pvalue)

See `PyLong_AsInt64() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_AsInt64>`__.

.. c:function:: int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue)

See `PyLong_AsUInt32() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_AsUInt32>`__.

.. c:function:: int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue)

See `PyLong_AsUInt64() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_AsUInt64>`__.

.. c:function:: PyObject* PyLong_FromInt32(int32_t value)

See `PyLong_FromInt32() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_FromInt32>`__.

.. c:function:: PyObject* PyLong_FromInt64(int64_t value)

See `PyLong_FromInt64() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_FromInt64>`__.

.. c:function:: PyObject* PyLong_FromUInt32(uint32_t value)

See `PyLong_FromUInt32() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_FromUInt32>`__.

.. c:function:: PyObject* PyLong_FromUInt64(uint64_t value)

See `PyLong_FromUInt64() documentation <https://docs.python.org/dev/c-api/long.html#c.PyLong_FromUInt64>`__.


Not supported:

* ``PyConfig_Get()``
Expand All @@ -108,14 +141,6 @@ Not supported:
* ``PyInitConfig_SetInt()``
* ``PyInitConfig_SetStr()``
* ``PyInitConfig_SetStrList()``
* ``PyLong_AsInt32()``
* ``PyLong_AsInt64()``
* ``PyLong_AsUInt32()``
* ``PyLong_AsUInt64()``
* ``PyLong_FromInt32()``
* ``PyLong_FromInt64()``
* ``PyLong_FromUInt32()``
* ``PyLong_FromUInt64()``
* ``PyType_GetBaseByToken()``
* ``PyUnicodeWriter_DecodeUTF8Stateful()``
* ``Py_InitializeFromInitConfig()``
Expand Down
8 changes: 8 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,14 @@ Changelog

* ``PyBytes_Join()``
* ``PyIter_NextItem()``
* ``PyLong_AsInt32()``
* ``PyLong_AsInt64()``
* ``PyLong_AsUInt32()``
* ``PyLong_AsUInt64()``
* ``PyLong_FromInt32()``
* ``PyLong_FromInt64()``
* ``PyLong_FromUInt32()``
* ``PyLong_FromUInt64()``
* ``PyUnicode_Equal()``
* ``Py_HashBuffer()``

Expand Down
85 changes: 85 additions & 0 deletions pythoncapi_compat.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,13 @@ extern "C" {
# define _PyObject_CAST(op) _Py_CAST(PyObject*, op)
#endif

#ifndef Py_BUILD_ASSERT
# define Py_BUILD_ASSERT(cond) \
do { \
(void)sizeof(char [1 - 2 * !(cond)]); \
} while(0)
#endif


// bpo-42262 added Py_NewRef() to Python 3.10.0a3
#if PY_VERSION_HEX < 0x030A00A3 && !defined(Py_NewRef)
Expand Down Expand Up @@ -1605,6 +1612,84 @@ static inline int PyIter_NextItem(PyObject *iter, PyObject **item)
#endif


#if PY_VERSION_HEX < 0x030E00A0
static inline PyObject* PyLong_FromInt32(int32_t value)
{
Py_BUILD_ASSERT(sizeof(long) >= 4);
return PyLong_FromLong(value);
}

static inline PyObject* PyLong_FromInt64(int64_t value)
{
Py_BUILD_ASSERT(sizeof(long long) >= 8);
return PyLong_FromLongLong(value);
}

static inline PyObject* PyLong_FromUInt32(uint32_t value)
{
Py_BUILD_ASSERT(sizeof(unsigned long) >= 4);
return PyLong_FromUnsignedLong(value);
}

static inline PyObject* PyLong_FromUInt64(uint64_t value)
{
Py_BUILD_ASSERT(sizeof(unsigned long long) >= 8);
return PyLong_FromUnsignedLongLong(value);
}

static inline int PyLong_AsInt32(PyObject *obj, int32_t *pvalue)
{
Py_BUILD_ASSERT(sizeof(int) == 4);
int value = PyLong_AsInt(obj);
if (value == -1 && PyErr_Occurred()) {
return -1;
}
*pvalue = (int32_t)value;
return 0;
}

static inline int PyLong_AsInt64(PyObject *obj, int64_t *pvalue)
{
Py_BUILD_ASSERT(sizeof(long long) == 8);
long long value = PyLong_AsLongLong(obj);
if (value == -1 && PyErr_Occurred()) {
return -1;
}
*pvalue = (int64_t)value;
return 0;
}

static inline int PyLong_AsUInt32(PyObject *obj, uint32_t *pvalue)
{
Py_BUILD_ASSERT(sizeof(long) >= 4);
unsigned long value = PyLong_AsUnsignedLong(obj);
if (value == (unsigned long)-1 && PyErr_Occurred()) {
return -1;
}
#if SIZEOF_LONG > 4
if ((unsigned long)UINT32_MAX < value) {
PyErr_SetString(PyExc_OverflowError,
"Python int too large to convert to C uint32_t");
return -1;
}
#endif
*pvalue = (uint32_t)value;
return 0;
}

static inline int PyLong_AsUInt64(PyObject *obj, uint64_t *pvalue)
{
Py_BUILD_ASSERT(sizeof(long long) == 8);
unsigned long long value = PyLong_AsUnsignedLongLong(obj);
if (value == (unsigned long long)-1 && PyErr_Occurred()) {
return -1;
}
*pvalue = (uint64_t)value;
return 0;
}
#endif


#ifdef __cplusplus
}
#endif
Expand Down
42 changes: 42 additions & 0 deletions tests/test_pythoncapi_compat_cext.c
Original file line number Diff line number Diff line change
Expand Up @@ -1971,6 +1971,47 @@ test_iter(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
}


static PyObject *
test_long_stdint(PyObject *Py_UNUSED(module), PyObject *Py_UNUSED(args))
{
PyObject *obj;

// Test PyLong_FromInt32() and PyLong_AsInt32()
obj = PyLong_FromInt32(INT32_C(-0x12345678));
assert(obj != NULL);
int32_t i32;
assert(PyLong_AsInt32(obj, &i32) == 0);
assert(i32 == INT32_C(-0x12345678));
Py_DECREF(obj);

// Test PyLong_FromUInt32() and PyLong_AsUInt32()
obj = PyLong_FromUInt32(UINT32_C(0xDEADBEEF));
assert(obj != NULL);
uint32_t u32;
assert(PyLong_AsUInt32(obj, &u32) == 0);
assert(u32 == UINT32_C(0xDEADBEEF));
Py_DECREF(obj);

// Test PyLong_FromInt64() and PyLong_AsInt64()
obj = PyLong_FromInt64(INT64_C(-0x12345678DEADBEEF));
assert(obj != NULL);
int64_t i64;
assert(PyLong_AsInt64(obj, &i64) == 0);
assert(i64 == INT64_C(-0x12345678DEADBEEF));
Py_DECREF(obj);

// Test PyLong_FromUInt64() and PyLong_AsUInt64()
obj = PyLong_FromUInt64(UINT64_C(0xDEADBEEF12345678));
assert(obj != NULL);
uint64_t u64;
assert(PyLong_AsUInt64(obj, &u64) == 0);
assert(u64 == UINT64_C(0xDEADBEEF12345678));
Py_DECREF(obj);

Py_RETURN_NONE;
}


static struct PyMethodDef methods[] = {
{"test_object", test_object, METH_NOARGS, _Py_NULL},
{"test_py_is", test_py_is, METH_NOARGS, _Py_NULL},
Expand Down Expand Up @@ -2016,6 +2057,7 @@ static struct PyMethodDef methods[] = {
#endif
{"test_bytes", test_bytes, METH_NOARGS, _Py_NULL},
{"test_iter", test_iter, METH_NOARGS, _Py_NULL},
{"test_long_stdint", test_long_stdint, METH_NOARGS, _Py_NULL},
{_Py_NULL, _Py_NULL, 0, _Py_NULL}
};

Expand Down

0 comments on commit 669c882

Please sign in to comment.