mirror of https://github.com/python/cpython
gh-111140: Adds PyLong_AsNativeBytes and PyLong_FromNative[Unsigned]Bytes functions (GH-114886)
This commit is contained in:
parent
a82fbc13d0
commit
7861dfd26a
|
@ -113,6 +113,28 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
|
||||||
retrieved from the resulting value using :c:func:`PyLong_AsVoidPtr`.
|
retrieved from the resulting value using :c:func:`PyLong_AsVoidPtr`.
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: PyObject* PyLong_FromNativeBytes(const void* buffer, size_t n_bytes, int endianness)
|
||||||
|
|
||||||
|
Create a Python integer from the value contained in the first *n_bytes* of
|
||||||
|
*buffer*, interpreted as a two's-complement signed number.
|
||||||
|
|
||||||
|
*endianness* may be passed ``-1`` for the native endian that CPython was
|
||||||
|
compiled with, or else ``0`` for big endian and ``1`` for little.
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: PyObject* PyLong_FromUnsignedNativeBytes(const void* buffer, size_t n_bytes, int endianness)
|
||||||
|
|
||||||
|
Create a Python integer from the value contained in the first *n_bytes* of
|
||||||
|
*buffer*, interpreted as an unsigned number.
|
||||||
|
|
||||||
|
*endianness* may be passed ``-1`` for the native endian that CPython was
|
||||||
|
compiled with, or else ``0`` for big endian and ``1`` for little.
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
|
||||||
.. XXX alias PyLong_AS_LONG (for now)
|
.. XXX alias PyLong_AS_LONG (for now)
|
||||||
.. c:function:: long PyLong_AsLong(PyObject *obj)
|
.. c:function:: long PyLong_AsLong(PyObject *obj)
|
||||||
|
|
||||||
|
@ -332,6 +354,50 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate.
|
||||||
Returns ``NULL`` on error. Use :c:func:`PyErr_Occurred` to disambiguate.
|
Returns ``NULL`` on error. Use :c:func:`PyErr_Occurred` to disambiguate.
|
||||||
|
|
||||||
|
|
||||||
|
.. c:function:: Py_ssize_t PyLong_AsNativeBytes(PyObject *pylong, void* buffer, Py_ssize_t n_bytes, int endianness)
|
||||||
|
|
||||||
|
Copy the Python integer value to a native *buffer* of size *n_bytes*::
|
||||||
|
|
||||||
|
int value;
|
||||||
|
Py_ssize_t bytes = PyLong_CopyBits(v, &value, sizeof(value), -1);
|
||||||
|
if (bytes < 0) {
|
||||||
|
// Error occurred
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
else if (bytes > sizeof(value)) {
|
||||||
|
// Overflow occurred, but 'value' contains as much as could fit
|
||||||
|
}
|
||||||
|
|
||||||
|
*endianness* may be passed ``-1`` for the native endian that CPython was
|
||||||
|
compiled with, or ``0`` for big endian and ``1`` for little.
|
||||||
|
|
||||||
|
Return ``-1`` with an exception raised if *pylong* cannot be interpreted as
|
||||||
|
an integer. Otherwise, return the size of the buffer required to store the
|
||||||
|
value. If this is equal to or less than *n_bytes*, the entire value was
|
||||||
|
copied.
|
||||||
|
|
||||||
|
Unless an exception is raised, all *n_bytes* of the buffer will be written
|
||||||
|
with as much of the value as can fit. This allows the caller to ignore all
|
||||||
|
non-negative results if the intent is to match the typical behavior of a
|
||||||
|
C-style downcast.
|
||||||
|
|
||||||
|
Values are always copied as twos-complement, and sufficient size will be
|
||||||
|
requested for a sign bit. For example, this may cause an value that fits into
|
||||||
|
8 bytes when treated as unsigned to request 9 bytes, even though all eight
|
||||||
|
bytes were copied into the buffer. What has been omitted is the zero sign
|
||||||
|
bit, which is redundant when the intention is to treat the value as unsigned.
|
||||||
|
|
||||||
|
Passing *n_bytes* of zero will always return the requested buffer size.
|
||||||
|
|
||||||
|
.. note::
|
||||||
|
|
||||||
|
When the value does not fit in the provided buffer, the requested size
|
||||||
|
returned from the function may be larger than necessary. Passing 0 to this
|
||||||
|
function is not an accurate way to determine the bit length of a value.
|
||||||
|
|
||||||
|
.. versionadded:: 3.13
|
||||||
|
|
||||||
|
|
||||||
.. c:function:: int PyUnstable_Long_IsCompact(const PyLongObject* op)
|
.. c:function:: int PyUnstable_Long_IsCompact(const PyLongObject* op)
|
||||||
|
|
||||||
Return 1 if *op* is compact, 0 otherwise.
|
Return 1 if *op* is compact, 0 otherwise.
|
||||||
|
|
|
@ -587,6 +587,7 @@ Tier 2 IR by Mark Shannon and Guido van Rossum.
|
||||||
Tier 2 optimizer by Ken Jin.)
|
Tier 2 optimizer by Ken Jin.)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Deprecated
|
Deprecated
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
@ -1526,6 +1527,11 @@ New Features
|
||||||
|
|
||||||
(Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.)
|
(Contributed by Victor Stinner and Petr Viktorin in :gh:`110850`.)
|
||||||
|
|
||||||
|
* Add :c:func:`PyLong_AsNativeBytes`, :c:func:`PyLong_FromNativeBytes` and
|
||||||
|
:c:func:`PyLong_FromUnsignedNativeBytes` functions to simplify converting
|
||||||
|
between native integer types and Python :class:`int` objects.
|
||||||
|
(Contributed by Steve Dower in :gh:`111140`.)
|
||||||
|
|
||||||
|
|
||||||
Porting to Python 3.13
|
Porting to Python 3.13
|
||||||
----------------------
|
----------------------
|
||||||
|
@ -1585,7 +1591,6 @@ Porting to Python 3.13
|
||||||
platforms, the ``HAVE_STDDEF_H`` macro is only defined on Windows.
|
platforms, the ``HAVE_STDDEF_H`` macro is only defined on Windows.
|
||||||
(Contributed by Victor Stinner in :gh:`108765`.)
|
(Contributed by Victor Stinner in :gh:`108765`.)
|
||||||
|
|
||||||
|
|
||||||
Deprecated
|
Deprecated
|
||||||
----------
|
----------
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,40 @@
|
||||||
|
|
||||||
PyAPI_FUNC(PyObject*) PyLong_FromUnicodeObject(PyObject *u, int base);
|
PyAPI_FUNC(PyObject*) PyLong_FromUnicodeObject(PyObject *u, int base);
|
||||||
|
|
||||||
|
/* PyLong_AsNativeBytes: Copy the integer value to a native variable.
|
||||||
|
buffer points to the first byte of the variable.
|
||||||
|
n_bytes is the number of bytes available in the buffer. Pass 0 to request
|
||||||
|
the required size for the value.
|
||||||
|
endianness is -1 for native endian, 0 for big endian or 1 for little.
|
||||||
|
Big endian mode will write the most significant byte into the address
|
||||||
|
directly referenced by buffer; little endian will write the least significant
|
||||||
|
byte into that address.
|
||||||
|
|
||||||
|
If an exception is raised, returns a negative value.
|
||||||
|
Otherwise, returns the number of bytes that are required to store the value.
|
||||||
|
To check that the full value is represented, ensure that the return value is
|
||||||
|
equal or less than n_bytes.
|
||||||
|
All n_bytes are guaranteed to be written (unless an exception occurs), and
|
||||||
|
so ignoring a positive return value is the equivalent of a downcast in C.
|
||||||
|
In cases where the full value could not be represented, the returned value
|
||||||
|
may be larger than necessary - this function is not an accurate way to
|
||||||
|
calculate the bit length of an integer object.
|
||||||
|
*/
|
||||||
|
PyAPI_FUNC(Py_ssize_t) PyLong_AsNativeBytes(PyObject* v, void* buffer,
|
||||||
|
Py_ssize_t n_bytes, int endianness);
|
||||||
|
|
||||||
|
/* PyLong_FromNativeBytes: Create an int value from a native integer
|
||||||
|
n_bytes is the number of bytes to read from the buffer. Passing 0 will
|
||||||
|
always produce the zero int.
|
||||||
|
PyLong_FromUnsignedNativeBytes always produces a non-negative int.
|
||||||
|
endianness is -1 for native endian, 0 for big endian or 1 for little.
|
||||||
|
|
||||||
|
Returns the int object, or NULL with an exception set. */
|
||||||
|
PyAPI_FUNC(PyObject*) PyLong_FromNativeBytes(const void* buffer, size_t n_bytes,
|
||||||
|
int endianness);
|
||||||
|
PyAPI_FUNC(PyObject*) PyLong_FromUnsignedNativeBytes(const void* buffer,
|
||||||
|
size_t n_bytes, int endianness);
|
||||||
|
|
||||||
PyAPI_FUNC(int) PyUnstable_Long_IsCompact(const PyLongObject* op);
|
PyAPI_FUNC(int) PyUnstable_Long_IsCompact(const PyLongObject* op);
|
||||||
PyAPI_FUNC(Py_ssize_t) PyUnstable_Long_CompactValue(const PyLongObject* op);
|
PyAPI_FUNC(Py_ssize_t) PyUnstable_Long_CompactValue(const PyLongObject* op);
|
||||||
|
|
||||||
|
@ -50,7 +84,7 @@ PyAPI_FUNC(PyObject *) _PyLong_FromByteArray(
|
||||||
*/
|
*/
|
||||||
PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v,
|
PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v,
|
||||||
unsigned char* bytes, size_t n,
|
unsigned char* bytes, size_t n,
|
||||||
int little_endian, int is_signed);
|
int little_endian, int is_signed, int with_exceptions);
|
||||||
|
|
||||||
/* For use by the gcd function in mathmodule.c */
|
/* For use by the gcd function in mathmodule.c */
|
||||||
PyAPI_FUNC(PyObject *) _PyLong_GCD(PyObject *, PyObject *);
|
PyAPI_FUNC(PyObject *) _PyLong_GCD(PyObject *, PyObject *);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import unittest
|
import unittest
|
||||||
import sys
|
import sys
|
||||||
|
import test.support as support
|
||||||
|
|
||||||
from test.support import import_helper
|
from test.support import import_helper
|
||||||
|
|
||||||
|
@ -423,6 +424,150 @@ class LongTests(unittest.TestCase):
|
||||||
self.assertRaises(OverflowError, asvoidptr, -2**1000)
|
self.assertRaises(OverflowError, asvoidptr, -2**1000)
|
||||||
# CRASHES asvoidptr(NULL)
|
# CRASHES asvoidptr(NULL)
|
||||||
|
|
||||||
|
def test_long_asnativebytes(self):
|
||||||
|
import math
|
||||||
|
from _testcapi import (
|
||||||
|
pylong_asnativebytes as asnativebytes,
|
||||||
|
SIZE_MAX,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Abbreviate sizeof(Py_ssize_t) to SZ because we use it a lot
|
||||||
|
SZ = int(math.ceil(math.log(SIZE_MAX + 1) / math.log(2)) / 8)
|
||||||
|
MAX_SSIZE = 2 ** (SZ * 8 - 1) - 1
|
||||||
|
MAX_USIZE = 2 ** (SZ * 8) - 1
|
||||||
|
if support.verbose:
|
||||||
|
print(f"SIZEOF_SIZE={SZ}\n{MAX_SSIZE=:016X}\n{MAX_USIZE=:016X}")
|
||||||
|
|
||||||
|
# These tests check that the requested buffer size is correct
|
||||||
|
for v, expect in [
|
||||||
|
(0, SZ),
|
||||||
|
(512, SZ),
|
||||||
|
(-512, SZ),
|
||||||
|
(MAX_SSIZE, SZ),
|
||||||
|
(MAX_USIZE, SZ + 1),
|
||||||
|
(-MAX_SSIZE, SZ),
|
||||||
|
(-MAX_USIZE, SZ + 1),
|
||||||
|
(2**255-1, 32),
|
||||||
|
(-(2**255-1), 32),
|
||||||
|
(2**256-1, 33),
|
||||||
|
(-(2**256-1), 33),
|
||||||
|
]:
|
||||||
|
with self.subTest(f"sizeof-{v:X}"):
|
||||||
|
buffer = bytearray(1)
|
||||||
|
self.assertEqual(expect, asnativebytes(v, buffer, 0, -1),
|
||||||
|
"PyLong_AsNativeBytes(v, NULL, 0, -1)")
|
||||||
|
# Also check via the __index__ path
|
||||||
|
self.assertEqual(expect, asnativebytes(Index(v), buffer, 0, -1),
|
||||||
|
"PyLong_AsNativeBytes(Index(v), NULL, 0, -1)")
|
||||||
|
|
||||||
|
# We request as many bytes as `expect_be` contains, and always check
|
||||||
|
# the result (both big and little endian). We check the return value
|
||||||
|
# independently, since the buffer should always be filled correctly even
|
||||||
|
# if we need more bytes
|
||||||
|
for v, expect_be, expect_n in [
|
||||||
|
(0, b'\x00', 1),
|
||||||
|
(0, b'\x00' * 2, 2),
|
||||||
|
(0, b'\x00' * 8, min(8, SZ)),
|
||||||
|
(1, b'\x01', 1),
|
||||||
|
(1, b'\x00' * 10 + b'\x01', min(11, SZ)),
|
||||||
|
(42, b'\x2a', 1),
|
||||||
|
(42, b'\x00' * 10 + b'\x2a', min(11, SZ)),
|
||||||
|
(-1, b'\xff', 1),
|
||||||
|
(-1, b'\xff' * 10, min(11, SZ)),
|
||||||
|
(-42, b'\xd6', 1),
|
||||||
|
(-42, b'\xff' * 10 + b'\xd6', min(11, SZ)),
|
||||||
|
# Extracts 255 into a single byte, but requests sizeof(Py_ssize_t)
|
||||||
|
(255, b'\xff', SZ),
|
||||||
|
(255, b'\x00\xff', 2),
|
||||||
|
(256, b'\x01\x00', 2),
|
||||||
|
# Extracts successfully (unsigned), but requests 9 bytes
|
||||||
|
(2**63, b'\x80' + b'\x00' * 7, 9),
|
||||||
|
# "Extracts", but requests 9 bytes
|
||||||
|
(-2**63, b'\x80' + b'\x00' * 7, 9),
|
||||||
|
(2**63, b'\x00\x80' + b'\x00' * 7, 9),
|
||||||
|
(-2**63, b'\xff\x80' + b'\x00' * 7, 9),
|
||||||
|
|
||||||
|
(2**255-1, b'\x7f' + b'\xff' * 31, 32),
|
||||||
|
(-(2**255-1), b'\x80' + b'\x00' * 30 + b'\x01', 32),
|
||||||
|
# Request extra bytes, but result says we only needed 32
|
||||||
|
(-(2**255-1), b'\xff\x80' + b'\x00' * 30 + b'\x01', 32),
|
||||||
|
(-(2**255-1), b'\xff\xff\x80' + b'\x00' * 30 + b'\x01', 32),
|
||||||
|
|
||||||
|
# Extracting 256 bits of integer will request 33 bytes, but still
|
||||||
|
# copy as many bits as possible into the buffer. So we *can* copy
|
||||||
|
# into a 32-byte buffer, though negative number may be unrecoverable
|
||||||
|
(2**256-1, b'\xff' * 32, 33),
|
||||||
|
(2**256-1, b'\x00' + b'\xff' * 32, 33),
|
||||||
|
(-(2**256-1), b'\x00' * 31 + b'\x01', 33),
|
||||||
|
(-(2**256-1), b'\xff' + b'\x00' * 31 + b'\x01', 33),
|
||||||
|
(-(2**256-1), b'\xff\xff' + b'\x00' * 31 + b'\x01', 33),
|
||||||
|
|
||||||
|
# The classic "Windows HRESULT as negative number" case
|
||||||
|
# HRESULT hr;
|
||||||
|
# PyLong_CopyBits(<-2147467259>, &hr, sizeof(HRESULT))
|
||||||
|
# assert(hr == E_FAIL)
|
||||||
|
(-2147467259, b'\x80\x00\x40\x05', 4),
|
||||||
|
]:
|
||||||
|
with self.subTest(f"{v:X}-{len(expect_be)}bytes"):
|
||||||
|
n = len(expect_be)
|
||||||
|
buffer = bytearray(n)
|
||||||
|
expect_le = expect_be[::-1]
|
||||||
|
|
||||||
|
self.assertEqual(expect_n, asnativebytes(v, buffer, n, 0),
|
||||||
|
f"PyLong_AsNativeBytes(v, buffer, {n}, <big>)")
|
||||||
|
self.assertEqual(expect_be, buffer[:n], "<big>")
|
||||||
|
self.assertEqual(expect_n, asnativebytes(v, buffer, n, 1),
|
||||||
|
f"PyLong_AsNativeBytes(v, buffer, {n}, <little>)")
|
||||||
|
self.assertEqual(expect_le, buffer[:n], "<little>")
|
||||||
|
|
||||||
|
# Check a few error conditions. These are validated in code, but are
|
||||||
|
# unspecified in docs, so if we make changes to the implementation, it's
|
||||||
|
# fine to just update these tests rather than preserve the behaviour.
|
||||||
|
with self.assertRaises(SystemError):
|
||||||
|
asnativebytes(1, buffer, 0, 2)
|
||||||
|
with self.assertRaises(TypeError):
|
||||||
|
asnativebytes('not a number', buffer, 0, -1)
|
||||||
|
|
||||||
|
def test_long_fromnativebytes(self):
|
||||||
|
import math
|
||||||
|
from _testcapi import (
|
||||||
|
pylong_fromnativebytes as fromnativebytes,
|
||||||
|
SIZE_MAX,
|
||||||
|
)
|
||||||
|
|
||||||
|
# Abbreviate sizeof(Py_ssize_t) to SZ because we use it a lot
|
||||||
|
SZ = int(math.ceil(math.log(SIZE_MAX + 1) / math.log(2)) / 8)
|
||||||
|
MAX_SSIZE = 2 ** (SZ * 8 - 1) - 1
|
||||||
|
MAX_USIZE = 2 ** (SZ * 8) - 1
|
||||||
|
|
||||||
|
for v_be, expect_s, expect_u in [
|
||||||
|
(b'\x00', 0, 0),
|
||||||
|
(b'\x01', 1, 1),
|
||||||
|
(b'\xff', -1, 255),
|
||||||
|
(b'\x00\xff', 255, 255),
|
||||||
|
(b'\xff\xff', -1, 65535),
|
||||||
|
]:
|
||||||
|
with self.subTest(f"{expect_s}-{expect_u:X}-{len(v_be)}bytes"):
|
||||||
|
n = len(v_be)
|
||||||
|
v_le = v_be[::-1]
|
||||||
|
|
||||||
|
self.assertEqual(expect_s, fromnativebytes(v_be, n, 0, 1),
|
||||||
|
f"PyLong_FromNativeBytes(buffer, {n}, <big>)")
|
||||||
|
self.assertEqual(expect_s, fromnativebytes(v_le, n, 1, 1),
|
||||||
|
f"PyLong_FromNativeBytes(buffer, {n}, <little>)")
|
||||||
|
self.assertEqual(expect_u, fromnativebytes(v_be, n, 0, 0),
|
||||||
|
f"PyLong_FromUnsignedNativeBytes(buffer, {n}, <big>)")
|
||||||
|
self.assertEqual(expect_u, fromnativebytes(v_le, n, 1, 0),
|
||||||
|
f"PyLong_FromUnsignedNativeBytes(buffer, {n}, <little>)")
|
||||||
|
|
||||||
|
# Check native endian when the result would be the same either
|
||||||
|
# way and we can test it.
|
||||||
|
if v_be == v_le:
|
||||||
|
self.assertEqual(expect_s, fromnativebytes(v_be, n, -1, 1),
|
||||||
|
f"PyLong_FromNativeBytes(buffer, {n}, <native>)")
|
||||||
|
self.assertEqual(expect_u, fromnativebytes(v_be, n, -1, 0),
|
||||||
|
f"PyLong_FromUnsignedNativeBytes(buffer, {n}, <native>)")
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
Adds :c:func:`PyLong_AsNativeBytes`, :c:func:`PyLong_FromNativeBytes` and
|
||||||
|
:c:func:`PyLong_FromUnsignedNativeBytes` functions.
|
|
@ -2393,7 +2393,7 @@ textiowrapper_parse_cookie(cookie_type *cookie, PyObject *cookieObj)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (_PyLong_AsByteArray(cookieLong, buffer, sizeof(buffer),
|
if (_PyLong_AsByteArray(cookieLong, buffer, sizeof(buffer),
|
||||||
PY_LITTLE_ENDIAN, 0) < 0) {
|
PY_LITTLE_ENDIAN, 0, 1) < 0) {
|
||||||
Py_DECREF(cookieLong);
|
Py_DECREF(cookieLong);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2162,7 +2162,8 @@ save_long(PicklerObject *self, PyObject *obj)
|
||||||
pdata = (unsigned char *)PyBytes_AS_STRING(repr);
|
pdata = (unsigned char *)PyBytes_AS_STRING(repr);
|
||||||
i = _PyLong_AsByteArray((PyLongObject *)obj,
|
i = _PyLong_AsByteArray((PyLongObject *)obj,
|
||||||
pdata, nbytes,
|
pdata, nbytes,
|
||||||
1 /* little endian */ , 1 /* signed */ );
|
1 /* little endian */ , 1 /* signed */ ,
|
||||||
|
1 /* with exceptions */);
|
||||||
if (i < 0)
|
if (i < 0)
|
||||||
goto error;
|
goto error;
|
||||||
/* If the int is negative, this may be a byte more than
|
/* If the int is negative, this may be a byte more than
|
||||||
|
|
|
@ -342,7 +342,8 @@ random_seed(RandomObject *self, PyObject *arg)
|
||||||
res = _PyLong_AsByteArray((PyLongObject *)n,
|
res = _PyLong_AsByteArray((PyLongObject *)n,
|
||||||
(unsigned char *)key, keyused * 4,
|
(unsigned char *)key, keyused * 4,
|
||||||
PY_LITTLE_ENDIAN,
|
PY_LITTLE_ENDIAN,
|
||||||
0); /* unsigned */
|
0, /* unsigned */
|
||||||
|
1); /* with exceptions */
|
||||||
if (res == -1) {
|
if (res == -1) {
|
||||||
goto Done;
|
goto Done;
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,7 @@ _pysqlite_long_as_int64(PyObject * py_val)
|
||||||
sqlite_int64 int64val;
|
sqlite_int64 int64val;
|
||||||
if (_PyLong_AsByteArray((PyLongObject *)py_val,
|
if (_PyLong_AsByteArray((PyLongObject *)py_val,
|
||||||
(unsigned char *)&int64val, sizeof(int64val),
|
(unsigned char *)&int64val, sizeof(int64val),
|
||||||
IS_LITTLE_ENDIAN, 1 /* signed */) >= 0) {
|
IS_LITTLE_ENDIAN, 1 /* signed */, 0) >= 0) {
|
||||||
return int64val;
|
return int64val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1000,9 +1000,10 @@ bp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
|
||||||
(unsigned char *)p,
|
(unsigned char *)p,
|
||||||
8,
|
8,
|
||||||
0, /* little_endian */
|
0, /* little_endian */
|
||||||
1 /* signed */);
|
1, /* signed */
|
||||||
|
0 /* !with_exceptions */);
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
if (res == -1 && PyErr_Occurred()) {
|
if (res < 0) {
|
||||||
PyErr_Format(state->StructError,
|
PyErr_Format(state->StructError,
|
||||||
"'%c' format requires %lld <= number <= %lld",
|
"'%c' format requires %lld <= number <= %lld",
|
||||||
f->format,
|
f->format,
|
||||||
|
@ -1024,9 +1025,10 @@ bp_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
|
||||||
(unsigned char *)p,
|
(unsigned char *)p,
|
||||||
8,
|
8,
|
||||||
0, /* little_endian */
|
0, /* little_endian */
|
||||||
0 /* signed */);
|
0, /* signed */
|
||||||
|
0 /* !with_exceptions */);
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
if (res == -1 && PyErr_Occurred()) {
|
if (res < 0) {
|
||||||
PyErr_Format(state->StructError,
|
PyErr_Format(state->StructError,
|
||||||
"'%c' format requires 0 <= number <= %llu",
|
"'%c' format requires 0 <= number <= %llu",
|
||||||
f->format,
|
f->format,
|
||||||
|
@ -1260,9 +1262,10 @@ lp_longlong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f)
|
||||||
(unsigned char *)p,
|
(unsigned char *)p,
|
||||||
8,
|
8,
|
||||||
1, /* little_endian */
|
1, /* little_endian */
|
||||||
1 /* signed */);
|
1, /* signed */
|
||||||
|
0 /* !with_exceptions */);
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
if (res == -1 && PyErr_Occurred()) {
|
if (res < 0) {
|
||||||
PyErr_Format(state->StructError,
|
PyErr_Format(state->StructError,
|
||||||
"'%c' format requires %lld <= number <= %lld",
|
"'%c' format requires %lld <= number <= %lld",
|
||||||
f->format,
|
f->format,
|
||||||
|
@ -1284,9 +1287,10 @@ lp_ulonglong(_structmodulestate *state, char *p, PyObject *v, const formatdef *f
|
||||||
(unsigned char *)p,
|
(unsigned char *)p,
|
||||||
8,
|
8,
|
||||||
1, /* little_endian */
|
1, /* little_endian */
|
||||||
0 /* signed */);
|
0, /* signed */
|
||||||
|
0 /* !with_exceptions */);
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
if (res == -1 && PyErr_Occurred()) {
|
if (res < 0) {
|
||||||
PyErr_Format(state->StructError,
|
PyErr_Format(state->StructError,
|
||||||
"'%c' format requires 0 <= number <= %llu",
|
"'%c' format requires 0 <= number <= %llu",
|
||||||
f->format,
|
f->format,
|
||||||
|
|
|
@ -776,6 +776,51 @@ pylong_asvoidptr(PyObject *module, PyObject *arg)
|
||||||
return Py_NewRef((PyObject *)value);
|
return Py_NewRef((PyObject *)value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
pylong_asnativebytes(PyObject *module, PyObject *args)
|
||||||
|
{
|
||||||
|
PyObject *v;
|
||||||
|
Py_buffer buffer;
|
||||||
|
Py_ssize_t n, endianness;
|
||||||
|
if (!PyArg_ParseTuple(args, "Ow*nn", &v, &buffer, &n, &endianness)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (buffer.readonly) {
|
||||||
|
PyErr_SetString(PyExc_TypeError, "buffer must be writable");
|
||||||
|
PyBuffer_Release(&buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (buffer.len < n) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "buffer must be at least 'n' bytes");
|
||||||
|
PyBuffer_Release(&buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
Py_ssize_t res = PyLong_AsNativeBytes(v, buffer.buf, n, (int)endianness);
|
||||||
|
PyBuffer_Release(&buffer);
|
||||||
|
return res >= 0 ? PyLong_FromSsize_t(res) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
pylong_fromnativebytes(PyObject *module, PyObject *args)
|
||||||
|
{
|
||||||
|
Py_buffer buffer;
|
||||||
|
Py_ssize_t n, endianness, signed_;
|
||||||
|
if (!PyArg_ParseTuple(args, "y*nnn", &buffer, &n, &endianness, &signed_)) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (buffer.len < n) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "buffer must be at least 'n' bytes");
|
||||||
|
PyBuffer_Release(&buffer);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PyObject *res = signed_
|
||||||
|
? PyLong_FromNativeBytes(buffer.buf, n, (int)endianness)
|
||||||
|
: PyLong_FromUnsignedNativeBytes(buffer.buf, n, (int)endianness);
|
||||||
|
PyBuffer_Release(&buffer);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyMethodDef test_methods[] = {
|
static PyMethodDef test_methods[] = {
|
||||||
_TESTCAPI_TEST_LONG_AND_OVERFLOW_METHODDEF
|
_TESTCAPI_TEST_LONG_AND_OVERFLOW_METHODDEF
|
||||||
_TESTCAPI_TEST_LONG_API_METHODDEF
|
_TESTCAPI_TEST_LONG_API_METHODDEF
|
||||||
|
@ -804,6 +849,8 @@ static PyMethodDef test_methods[] = {
|
||||||
{"pylong_as_size_t", pylong_as_size_t, METH_O},
|
{"pylong_as_size_t", pylong_as_size_t, METH_O},
|
||||||
{"pylong_asdouble", pylong_asdouble, METH_O},
|
{"pylong_asdouble", pylong_asdouble, METH_O},
|
||||||
{"pylong_asvoidptr", pylong_asvoidptr, METH_O},
|
{"pylong_asvoidptr", pylong_asvoidptr, METH_O},
|
||||||
|
{"pylong_asnativebytes", pylong_asnativebytes, METH_VARARGS},
|
||||||
|
{"pylong_fromnativebytes", pylong_fromnativebytes, METH_VARARGS},
|
||||||
{NULL},
|
{NULL},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -813,6 +860,5 @@ _PyTestCapi_Init_Long(PyObject *mod)
|
||||||
if (PyModule_AddFunctions(mod, test_methods) < 0) {
|
if (PyModule_AddFunctions(mod, test_methods) < 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -926,7 +926,8 @@ AsObj(PyObject *value)
|
||||||
(unsigned char *)(void *)&wideValue,
|
(unsigned char *)(void *)&wideValue,
|
||||||
sizeof(wideValue),
|
sizeof(wideValue),
|
||||||
PY_LITTLE_ENDIAN,
|
PY_LITTLE_ENDIAN,
|
||||||
/* signed */ 1) == 0) {
|
/* signed */ 1,
|
||||||
|
/* with_exceptions */ 1) == 0) {
|
||||||
return Tcl_NewWideIntObj(wideValue);
|
return Tcl_NewWideIntObj(wideValue);
|
||||||
}
|
}
|
||||||
PyErr_Clear();
|
PyErr_Clear();
|
||||||
|
|
|
@ -973,7 +973,8 @@ _multibytecodec_MultibyteIncrementalEncoder_setstate_impl(MultibyteIncrementalEn
|
||||||
|
|
||||||
if (_PyLong_AsByteArray(statelong, statebytes, sizeof(statebytes),
|
if (_PyLong_AsByteArray(statelong, statebytes, sizeof(statebytes),
|
||||||
1 /* little-endian */ ,
|
1 /* little-endian */ ,
|
||||||
0 /* unsigned */ ) < 0) {
|
0 /* unsigned */ ,
|
||||||
|
1 /* with_exceptions */) < 0) {
|
||||||
goto errorexit;
|
goto errorexit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1255,7 +1256,8 @@ _multibytecodec_MultibyteIncrementalDecoder_setstate_impl(MultibyteIncrementalDe
|
||||||
|
|
||||||
if (_PyLong_AsByteArray(statelong, statebytes, sizeof(statebytes),
|
if (_PyLong_AsByteArray(statelong, statebytes, sizeof(statebytes),
|
||||||
1 /* little-endian */ ,
|
1 /* little-endian */ ,
|
||||||
0 /* unsigned */ ) < 0) {
|
0 /* unsigned */ ,
|
||||||
|
1 /* with_exceptions */) < 0) {
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -928,7 +928,8 @@ _PyLong_FromByteArray(const unsigned char* bytes, size_t n,
|
||||||
int
|
int
|
||||||
_PyLong_AsByteArray(PyLongObject* v,
|
_PyLong_AsByteArray(PyLongObject* v,
|
||||||
unsigned char* bytes, size_t n,
|
unsigned char* bytes, size_t n,
|
||||||
int little_endian, int is_signed)
|
int little_endian, int is_signed,
|
||||||
|
int with_exceptions)
|
||||||
{
|
{
|
||||||
Py_ssize_t i; /* index into v->long_value.ob_digit */
|
Py_ssize_t i; /* index into v->long_value.ob_digit */
|
||||||
Py_ssize_t ndigits; /* number of digits */
|
Py_ssize_t ndigits; /* number of digits */
|
||||||
|
@ -945,8 +946,10 @@ _PyLong_AsByteArray(PyLongObject* v,
|
||||||
ndigits = _PyLong_DigitCount(v);
|
ndigits = _PyLong_DigitCount(v);
|
||||||
if (_PyLong_IsNegative(v)) {
|
if (_PyLong_IsNegative(v)) {
|
||||||
if (!is_signed) {
|
if (!is_signed) {
|
||||||
PyErr_SetString(PyExc_OverflowError,
|
if (with_exceptions) {
|
||||||
"can't convert negative int to unsigned");
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
|
"can't convert negative int to unsigned");
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
do_twos_comp = 1;
|
do_twos_comp = 1;
|
||||||
|
@ -967,7 +970,12 @@ _PyLong_AsByteArray(PyLongObject* v,
|
||||||
/* Copy over all the Python digits.
|
/* Copy over all the Python digits.
|
||||||
It's crucial that every Python digit except for the MSD contribute
|
It's crucial that every Python digit except for the MSD contribute
|
||||||
exactly PyLong_SHIFT bits to the total, so first assert that the int is
|
exactly PyLong_SHIFT bits to the total, so first assert that the int is
|
||||||
normalized. */
|
normalized.
|
||||||
|
NOTE: PyLong_AsNativeBytes() assumes that this function will fill in 'n'
|
||||||
|
bytes even if it eventually fails to convert the whole number. Make sure
|
||||||
|
you account for that if you are changing this algorithm to return without
|
||||||
|
doing that.
|
||||||
|
*/
|
||||||
assert(ndigits == 0 || v->long_value.ob_digit[ndigits - 1] != 0);
|
assert(ndigits == 0 || v->long_value.ob_digit[ndigits - 1] != 0);
|
||||||
j = 0;
|
j = 0;
|
||||||
accum = 0;
|
accum = 0;
|
||||||
|
@ -1052,11 +1060,203 @@ _PyLong_AsByteArray(PyLongObject* v,
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
Overflow:
|
Overflow:
|
||||||
PyErr_SetString(PyExc_OverflowError, "int too big to convert");
|
if (with_exceptions) {
|
||||||
|
PyErr_SetString(PyExc_OverflowError, "int too big to convert");
|
||||||
|
}
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Refactored out for readability, not reuse
|
||||||
|
static inline int
|
||||||
|
_fits_in_n_bits(Py_ssize_t v, Py_ssize_t n)
|
||||||
|
{
|
||||||
|
if (n >= (Py_ssize_t)sizeof(Py_ssize_t) * 8) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
// If all bits above n are the same, we fit.
|
||||||
|
// (Use n-1 if we require the sign bit to be consistent.)
|
||||||
|
Py_ssize_t v_extended = v >> ((int)n - 1);
|
||||||
|
return v_extended == 0 || v_extended == -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int
|
||||||
|
_resolve_endianness(int *endianness)
|
||||||
|
{
|
||||||
|
if (*endianness < 0) {
|
||||||
|
*endianness = PY_LITTLE_ENDIAN;
|
||||||
|
}
|
||||||
|
if (*endianness != 0 && *endianness != 1) {
|
||||||
|
PyErr_SetString(PyExc_SystemError, "invalid 'endianness' value");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Py_ssize_t
|
||||||
|
PyLong_AsNativeBytes(PyObject* vv, void* buffer, Py_ssize_t n, int endianness)
|
||||||
|
{
|
||||||
|
PyLongObject *v;
|
||||||
|
union {
|
||||||
|
Py_ssize_t v;
|
||||||
|
unsigned char b[sizeof(Py_ssize_t)];
|
||||||
|
} cv;
|
||||||
|
int do_decref = 0;
|
||||||
|
Py_ssize_t res = 0;
|
||||||
|
|
||||||
|
if (vv == NULL || n < 0) {
|
||||||
|
PyErr_BadInternalCall();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int little_endian = endianness;
|
||||||
|
if (_resolve_endianness(&little_endian) < 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyLong_Check(vv)) {
|
||||||
|
v = (PyLongObject *)vv;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
v = (PyLongObject *)_PyNumber_Index(vv);
|
||||||
|
if (v == NULL) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
do_decref = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_PyLong_IsCompact(v)) {
|
||||||
|
res = 0;
|
||||||
|
cv.v = _PyLong_CompactValue(v);
|
||||||
|
/* Most paths result in res = sizeof(compact value). Only the case
|
||||||
|
* where 0 < n < sizeof(compact value) do we need to check and adjust
|
||||||
|
* our return value. */
|
||||||
|
res = sizeof(cv.b);
|
||||||
|
if (n <= 0) {
|
||||||
|
// nothing to do!
|
||||||
|
}
|
||||||
|
else if (n <= sizeof(cv.b)) {
|
||||||
|
#if PY_LITTLE_ENDIAN
|
||||||
|
if (little_endian) {
|
||||||
|
memcpy(buffer, cv.b, n);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (Py_ssize_t i = 0; i < n; ++i) {
|
||||||
|
((unsigned char*)buffer)[n - i - 1] = cv.b[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (little_endian) {
|
||||||
|
for (Py_ssize_t i = 0; i < n; ++i) {
|
||||||
|
((unsigned char*)buffer)[i] = cv.b[sizeof(cv.b) - i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memcpy(buffer, &cv.b[sizeof(cv.b) - n], n);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/* If we fit, return the requested number of bytes */
|
||||||
|
if (_fits_in_n_bits(cv.v, n * 8)) {
|
||||||
|
res = n;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned char fill = cv.v < 0 ? 0xFF : 0x00;
|
||||||
|
#if PY_LITTLE_ENDIAN
|
||||||
|
if (little_endian) {
|
||||||
|
memcpy(buffer, cv.b, sizeof(cv.b));
|
||||||
|
memset((char *)buffer + sizeof(cv.b), fill, n - sizeof(cv.b));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
unsigned char *b = (unsigned char *)buffer;
|
||||||
|
for (Py_ssize_t i = 0; i < n - (int)sizeof(cv.b); ++i) {
|
||||||
|
*b++ = fill;
|
||||||
|
}
|
||||||
|
for (Py_ssize_t i = sizeof(cv.b); i > 0; --i) {
|
||||||
|
*b++ = cv.b[i - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if (little_endian) {
|
||||||
|
unsigned char *b = (unsigned char *)buffer;
|
||||||
|
for (Py_ssize_t i = sizeof(cv.b); i > 0; --i) {
|
||||||
|
*b++ = cv.b[i - 1];
|
||||||
|
}
|
||||||
|
for (Py_ssize_t i = 0; i < n - sizeof(cv.b); ++i) {
|
||||||
|
*b++ = fill;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
memset(buffer, fill, n - sizeof(cv.b));
|
||||||
|
memcpy((char *)buffer + n - sizeof(cv.b), cv.b, sizeof(cv.b));
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (n > 0) {
|
||||||
|
_PyLong_AsByteArray(v, buffer, (size_t)n, little_endian, 1, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// More efficient calculation for number of bytes required?
|
||||||
|
size_t nb = _PyLong_NumBits((PyObject *)v);
|
||||||
|
/* Normally this would be((nb - 1) / 8) + 1 to avoid rounding up
|
||||||
|
* multiples of 8 to the next byte, but we add an implied bit for
|
||||||
|
* the sign and it cancels out. */
|
||||||
|
size_t n_needed = (nb / 8) + 1;
|
||||||
|
res = (Py_ssize_t)n_needed;
|
||||||
|
if ((size_t)res != n_needed) {
|
||||||
|
PyErr_SetString(PyExc_OverflowError,
|
||||||
|
"value too large to convert");
|
||||||
|
res = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (do_decref) {
|
||||||
|
Py_DECREF(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyLong_FromNativeBytes(const void* buffer, size_t n, int endianness)
|
||||||
|
{
|
||||||
|
if (!buffer) {
|
||||||
|
PyErr_BadInternalCall();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int little_endian = endianness;
|
||||||
|
if (_resolve_endianness(&little_endian) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _PyLong_FromByteArray((const unsigned char *)buffer, n,
|
||||||
|
little_endian, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PyObject *
|
||||||
|
PyLong_FromUnsignedNativeBytes(const void* buffer, size_t n, int endianness)
|
||||||
|
{
|
||||||
|
if (!buffer) {
|
||||||
|
PyErr_BadInternalCall();
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int little_endian = endianness;
|
||||||
|
if (_resolve_endianness(&little_endian) < 0) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return _PyLong_FromByteArray((const unsigned char *)buffer, n,
|
||||||
|
little_endian, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Create a new int object from a C pointer */
|
/* Create a new int object from a C pointer */
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
|
@ -1231,7 +1431,7 @@ PyLong_AsLongLong(PyObject *vv)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res = _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)&bytes,
|
res = _PyLong_AsByteArray((PyLongObject *)v, (unsigned char *)&bytes,
|
||||||
SIZEOF_LONG_LONG, PY_LITTLE_ENDIAN, 1);
|
SIZEOF_LONG_LONG, PY_LITTLE_ENDIAN, 1, 1);
|
||||||
}
|
}
|
||||||
if (do_decref) {
|
if (do_decref) {
|
||||||
Py_DECREF(v);
|
Py_DECREF(v);
|
||||||
|
@ -1270,7 +1470,7 @@ PyLong_AsUnsignedLongLong(PyObject *vv)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes,
|
res = _PyLong_AsByteArray((PyLongObject *)vv, (unsigned char *)&bytes,
|
||||||
SIZEOF_LONG_LONG, PY_LITTLE_ENDIAN, 0);
|
SIZEOF_LONG_LONG, PY_LITTLE_ENDIAN, 0, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Plan 9 can't handle long long in ? : expressions */
|
/* Plan 9 can't handle long long in ? : expressions */
|
||||||
|
@ -6068,7 +6268,7 @@ int_to_bytes_impl(PyObject *self, Py_ssize_t length, PyObject *byteorder,
|
||||||
|
|
||||||
if (_PyLong_AsByteArray((PyLongObject *)self,
|
if (_PyLong_AsByteArray((PyLongObject *)self,
|
||||||
(unsigned char *)PyBytes_AS_STRING(bytes),
|
(unsigned char *)PyBytes_AS_STRING(bytes),
|
||||||
length, little_endian, is_signed) < 0) {
|
length, little_endian, is_signed, 1) < 0) {
|
||||||
Py_DECREF(bytes);
|
Py_DECREF(bytes);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue