Skip to content

Commit

Permalink
pythongh-106320: Re-add some PyLong/PyDict C-API functions (GH-python…
Browse files Browse the repository at this point in the history
…#111162)

* pythongh-106320: Re-add _PyLong_FromByteArray(), _PyLong_AsByteArray() and _PyLong_GCD() to the public header files since they are used by third-party packages and there is no efficient replacement.

See python#111140
See python#111139

* pythongh-111262: Re-add _PyDict_Pop() to have a C-API until a new public one is designed.
  • Loading branch information
scoder authored and aisk committed Feb 11, 2024
1 parent c6b839e commit 0956570
Show file tree
Hide file tree
Showing 10 changed files with 44 additions and 55 deletions.
1 change: 1 addition & 0 deletions Include/cpython/dictobject.h
Expand Up @@ -46,6 +46,7 @@ static inline Py_ssize_t PyDict_GET_SIZE(PyObject *op) {

PyAPI_FUNC(int) PyDict_ContainsString(PyObject *mp, const char *key);

PyAPI_FUNC(PyObject *) _PyDict_Pop(PyObject *dict, PyObject *key, PyObject *default_value);

/* Dictionary watchers */

Expand Down
42 changes: 42 additions & 0 deletions Include/cpython/longobject.h
Expand Up @@ -7,3 +7,45 @@ PyAPI_FUNC(PyObject*) PyLong_FromUnicodeObject(PyObject *u, int base);
PyAPI_FUNC(int) PyUnstable_Long_IsCompact(const PyLongObject* op);
PyAPI_FUNC(Py_ssize_t) PyUnstable_Long_CompactValue(const PyLongObject* op);

/* _PyLong_FromByteArray: View the n unsigned bytes as a binary integer in
base 256, and return a Python int with the same numeric value.
If n is 0, the integer is 0. Else:
If little_endian is 1/true, bytes[n-1] is the MSB and bytes[0] the LSB;
else (little_endian is 0/false) bytes[0] is the MSB and bytes[n-1] the
LSB.
If is_signed is 0/false, view the bytes as a non-negative integer.
If is_signed is 1/true, view the bytes as a 2's-complement integer,
non-negative if bit 0x80 of the MSB is clear, negative if set.
Error returns:
+ Return NULL with the appropriate exception set if there's not
enough memory to create the Python int.
*/
PyAPI_FUNC(PyObject *) _PyLong_FromByteArray(
const unsigned char* bytes, size_t n,
int little_endian, int is_signed);

/* _PyLong_AsByteArray: Convert the least-significant 8*n bits of long
v to a base-256 integer, stored in array bytes. Normally return 0,
return -1 on error.
If little_endian is 1/true, store the MSB at bytes[n-1] and the LSB at
bytes[0]; else (little_endian is 0/false) store the MSB at bytes[0] and
the LSB at bytes[n-1].
If is_signed is 0/false, it's an error if v < 0; else (v >= 0) n bytes
are filled and there's nothing special about bit 0x80 of the MSB.
If is_signed is 1/true, bytes is filled with the 2's-complement
representation of v's value. Bit 0x80 of the MSB is the sign bit.
Error returns (-1):
+ is_signed is 0 and v < 0. TypeError is set in this case, and bytes
isn't altered.
+ n isn't big enough to hold the full mathematical value of v. For
example, if is_signed is 0 and there are more digits in the v than
fit in n; or if is_signed is 1, v < 0, and n is just 1 bit shy of
being large enough to hold a sign bit. OverflowError is set in this
case, but bytes holds the least-significant n bytes of the true value.
*/
PyAPI_FUNC(int) _PyLong_AsByteArray(PyLongObject* v,
unsigned char* bytes, size_t n,
int little_endian, int is_signed);

/* For use by the gcd function in mathmodule.c */
PyAPI_FUNC(PyObject *) _PyLong_GCD(PyObject *, PyObject *);
3 changes: 0 additions & 3 deletions Include/internal/pycore_dict.h
Expand Up @@ -49,9 +49,6 @@ extern PyObject* _PyDict_NewPresized(Py_ssize_t minused);
// Export for '_ctypes' shared extension
PyAPI_FUNC(Py_ssize_t) _PyDict_SizeOf(PyDictObject *);

// Export for '_socket' shared extension (Windows remove_unusable_flags())
PyAPI_FUNC(PyObject*) _PyDict_Pop(PyObject *, PyObject *, PyObject *);

#define _PyDict_HasSplitTable(d) ((d)->ma_values != NULL)

/* Like PyDict_Merge, but override can be 0, 1 or 2. If override is 0,
Expand Down
46 changes: 0 additions & 46 deletions Include/internal/pycore_long.h
Expand Up @@ -128,57 +128,11 @@ extern PyObject* _PyLong_FromBytes(const char *, Py_ssize_t, int);
// Export for '_datetime' shared extension.
PyAPI_DATA(PyObject*) _PyLong_DivmodNear(PyObject *, PyObject *);

// _PyLong_FromByteArray: View the n unsigned bytes as a binary integer in
// base 256, and return a Python int with the same numeric value.
// If n is 0, the integer is 0. Else:
// If little_endian is 1/true, bytes[n-1] is the MSB and bytes[0] the LSB;
// else (little_endian is 0/false) bytes[0] is the MSB and bytes[n-1] the
// LSB.
// If is_signed is 0/false, view the bytes as a non-negative integer.
// If is_signed is 1/true, view the bytes as a 2's-complement integer,
// non-negative if bit 0x80 of the MSB is clear, negative if set.
// Error returns:
// + Return NULL with the appropriate exception set if there's not
// enough memory to create the Python int.
//
// Export for '_multibytecodec' shared extension.
PyAPI_DATA(PyObject*) _PyLong_FromByteArray(
const unsigned char* bytes, size_t n,
int little_endian, int is_signed);

// _PyLong_AsByteArray: Convert the least-significant 8*n bits of long
// v to a base-256 integer, stored in array bytes. Normally return 0,
// return -1 on error.
// If little_endian is 1/true, store the MSB at bytes[n-1] and the LSB at
// bytes[0]; else (little_endian is 0/false) store the MSB at bytes[0] and
// the LSB at bytes[n-1].
// If is_signed is 0/false, it's an error if v < 0; else (v >= 0) n bytes
// are filled and there's nothing special about bit 0x80 of the MSB.
// If is_signed is 1/true, bytes is filled with the 2's-complement
// representation of v's value. Bit 0x80 of the MSB is the sign bit.
// Error returns (-1):
// + is_signed is 0 and v < 0. TypeError is set in this case, and bytes
// isn't altered.
// + n isn't big enough to hold the full mathematical value of v. For
// example, if is_signed is 0 and there are more digits in the v than
// fit in n; or if is_signed is 1, v < 0, and n is just 1 bit shy of
// being large enough to hold a sign bit. OverflowError is set in this
// case, but bytes holds the least-significant n bytes of the true value.
//
// Export for '_struct' shared extension.
PyAPI_DATA(int) _PyLong_AsByteArray(PyLongObject* v,
unsigned char* bytes, size_t n,
int little_endian, int is_signed);

// _PyLong_Format: Convert the long to a string object with given base,
// appending a base prefix of 0[box] if base is 2, 8 or 16.
// Export for '_tkinter' shared extension.
PyAPI_DATA(PyObject*) _PyLong_Format(PyObject *obj, int base);

// For use by the math.gcd() function.
// Export for 'math' shared extension.
PyAPI_DATA(PyObject*) _PyLong_GCD(PyObject *, PyObject *);

// Export for 'math' shared extension
PyAPI_DATA(PyObject*) _PyLong_Rshift(PyObject *, size_t);

Expand Down
2 changes: 1 addition & 1 deletion Modules/_randommodule.c
Expand Up @@ -71,7 +71,7 @@
#endif

#include "Python.h"
#include "pycore_long.h" // _PyLong_AsByteArray()
#include "pycore_long.h" // _PyLong_NumBits()
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_pylifecycle.h" // _PyOS_URandomNonblock()
Expand Down
1 change: 0 additions & 1 deletion Modules/_threadmodule.c
Expand Up @@ -3,7 +3,6 @@
/* Interface to Sjoerd's portable C thread library */

#include "Python.h"
#include "pycore_dict.h" // _PyDict_Pop()
#include "pycore_interp.h" // _PyInterpreterState.threads.count
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_pyerrors.h" // _PyErr_WriteUnraisableMsg()
Expand Down
1 change: 0 additions & 1 deletion Modules/arraymodule.c
Expand Up @@ -11,7 +11,6 @@
#include "pycore_bytesobject.h" // _PyBytes_Repeat
#include "pycore_call.h" // _PyObject_CallMethod()
#include "pycore_ceval.h" // _PyEval_GetBuiltin()
#include "pycore_long.h" // _PyLong_FromByteArray()
#include "pycore_modsupport.h" // _PyArg_NoKeywords()
#include "pycore_moduleobject.h" // _PyModule_GetState()

Expand Down
1 change: 0 additions & 1 deletion Modules/cjkcodecs/multibytecodec.c
Expand Up @@ -9,7 +9,6 @@
#endif

#include "Python.h"
#include "pycore_long.h" // _PyLong_FromByteArray()

#include "multibytecodec.h"
#include "clinic/multibytecodec.c.h"
Expand Down
1 change: 0 additions & 1 deletion Modules/socketmodule.c
Expand Up @@ -107,7 +107,6 @@ Local naming conventions:

#include "Python.h"
#include "pycore_capsule.h" // _PyCapsule_SetTraverse()
#include "pycore_dict.h" // _PyDict_Pop()
#include "pycore_fileutils.h" // _Py_set_inheritable()
#include "pycore_moduleobject.h" // _PyModule_GetState

Expand Down
1 change: 0 additions & 1 deletion Python/import.c
@@ -1,7 +1,6 @@
/* Module definition and import implementation */

#include "Python.h"
#include "pycore_dict.h" // _PyDict_Pop()
#include "pycore_hashtable.h" // _Py_hashtable_new_full()
#include "pycore_import.h" // _PyImport_BootstrapImp()
#include "pycore_initconfig.h" // _PyStatus_OK()
Expand Down

0 comments on commit 0956570

Please sign in to comment.