Skip to content

Add PyLong_FromUInt64() and PyLong_AsUInt64() #114

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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