diff options
author | Gregory P. Smith <greg@krypto.org> | 2024-02-22 03:27:16 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-22 03:27:16 (GMT) |
commit | fac99b8b0df209ca6546545193b1873672d536ca (patch) | |
tree | 8f5861020e44f45a8cdf5c5af004750c4aa57d96 /Doc/c-api/long.rst | |
parent | 7f5e3f04f838686d65f1053a5e47f5d3faf0b228 (diff) | |
download | cpython-fac99b8b0df209ca6546545193b1873672d536ca.zip cpython-fac99b8b0df209ca6546545193b1873672d536ca.tar.gz cpython-fac99b8b0df209ca6546545193b1873672d536ca.tar.bz2 |
gh-111140: Improve PyLong_AsNativeBytes API doc example & improve the test (#115380)
This expands the examples to cover both realistic use cases for the API.
I noticed thing in the test that could be done better so I added those as well: We need to guarantee that all bytes of the result are overwritten and that too many are not written. Tests now pre-fills the result with data in order to ensure that.
Co-authored-by: Steve Dower <steve.dower@microsoft.com>
Diffstat (limited to 'Doc/c-api/long.rst')
-rw-r--r-- | Doc/c-api/long.rst | 74 |
1 files changed, 57 insertions, 17 deletions
diff --git a/Doc/c-api/long.rst b/Doc/c-api/long.rst index f24282e..582f5c7 100644 --- a/Doc/c-api/long.rst +++ b/Doc/c-api/long.rst @@ -358,46 +358,86 @@ distinguished from a number. Use :c:func:`PyErr_Occurred` to disambiguate. Copy the Python integer value to a native *buffer* of size *n_bytes*:: - int value; - Py_ssize_t bytes = PyLong_AsNativeBytes(v, &value, sizeof(value), -1); + int32_t value; + Py_ssize_t bytes = PyLong_AsNativeBits(pylong, &value, sizeof(value), -1); if (bytes < 0) { - // Error occurred + // A Python exception was set with the reason. return NULL; } else if (bytes <= (Py_ssize_t)sizeof(value)) { // Success! } else { - // Overflow occurred, but 'value' contains truncated value + // Overflow occurred, but 'value' contains the truncated + // lowest bits of pylong. } + The above example may look *similar* to + :c:func:`PyLong_As* <PyLong_AsSize_t>` + but instead fills in a specific caller defined type and never raises an + error about of the :class:`int` *pylong*'s value regardless of *n_bytes* + or the returned byte count. + + To get at the entire potentially big Python value, this can be used to + reserve enough space and copy it:: + + // Ask how much space we need. + Py_ssize_t expected = PyLong_AsNativeBits(pylong, NULL, 0, -1); + if (expected < 0) { + // A Python exception was set with the reason. + return NULL; + } + assert(expected != 0); // Impossible per the API definition. + uint8_t *bignum = malloc(expected); + if (!bignum) { + PyErr_SetString(PyExc_MemoryError, "bignum malloc failed."); + return NULL; + } + // Safely get the entire value. + Py_ssize_t bytes = PyLong_AsNativeBits(pylong, bignum, expected, -1); + if (bytes < 0) { // Exception set. + free(bignum); + return NULL; + } + else if (bytes > expected) { // Be safe, should not be possible. + PyErr_SetString(PyExc_RuntimeError, + "Unexpected bignum truncation after a size check."); + free(bignum); + return NULL; + } + // The expected success given the above pre-check. + // ... use bignum ... + free(bignum); + *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 + Returns ``-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. + copied. ``0`` will never be returned. - 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. No exception is set for this case. + Unless an exception is raised, all *n_bytes* of the buffer will always be + written. In the case of truncation, as many of the lowest bits of the value + as could fit are written. This allows the caller to ignore all non-negative + results if the intent is to match the typical behavior of a C-style + downcast. No exception is set on truncation. - Values are always copied as two's-complement, and sufficient buffer will be + Values are always copied as two's-complement and sufficient buffer will be requested to include 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. + zero sign bit -- redundant if the caller's intention is to treat the value + as unsigned. - Passing zero to *n_bytes* will return the requested buffer size. + Passing zero to *n_bytes* will return the size of a buffer that would + be large enough to hold the value. This may be larger than technically + necessary, but not unreasonably so. .. 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. + Passing *n_bytes=0* to this function is not an accurate way to determine + the bit length of a value. .. versionadded:: 3.13 |